Commit 3e20240b authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

ERP5Security: Use a dedicated Login document to handle authentication.

This commit is the squashed result of the following commits :

  commit cf26c55ab3bb8b97d690e45c800d17341150024e
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Thu Oct 13 04:25:12 2016 +0000

      erp5_crendential: Clean up XML because were migrated to the new format in 76ecef89

  commit 7f16068be004222d7d239948dd3a2633ecf9e06d
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Thu Oct 13 03:08:38 2016 +0000

      erp5_base: Move document Login.py to portal_components

  commit e1618e091279dd803a5777ea701afb310d2aa31d
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Mon Oct 10 13:40:11 2016 +0000

      testERP5Securiy: install erp5_administration because is requried to testSimpleLocalRole

  commit 3fa7360568968c4a011d2403be0c0346b3ca35bb
  Author: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
  Date:   Wed Oct 5 13:18:12 2016 +0000

      test: display Base_viewSecurity body in case of assertion failure.

  commit d0b88687c139eec4bb07d2c328ca86379e9f9335
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Mon Oct 3 19:18:37 2016 +0000

      ERP5UserManager: store relative_url instead of object in _person_cache because is always expected an url in this cache

  commit 3993fadae344aaa4dfa0e6bd73683485c289f976
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Mon Oct 3 17:22:51 2016 +0000

      erp5_authentication_policy: Migrate scripts to new format

  commit 7ad5407d0fd40f849d1142be9ae43ac0dd42445b
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Mon Oct 3 15:52:47 2016 +0000

      erp5_base: Fix workflow transitions that are not guarded

      ======================================================================
      FAIL: test_workflow_transition_protection (testSecurity.TestSecurity)
      ----------------------------------------------------------------------
      Traceback (most recent call last):
      File "/srv/slapgrid/slappart75/srv/runner/software/7b5ffcc34442ea1842e916493cae988d/parts/erp5/Products/ERP5/tests/testSecurity.py", line 131, in test_workflow_transition_protection
      self.fail(message)
      AssertionError:
      The following 3 workflow transitions are not guarded.
      login_validation_workflow/transitions/delete
      login_validation_workflow/transitions/invalidate
      login_validation_workflow/transitions/validate

  commit 4f6fc71eac8417092144ce5aedb9aa41cd1c26da
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Wed Aug 17 15:46:55 2016 +0000

      erp5_core: migrate scripts to new format

  commit 158ad0110463075be89f56c0d6e020e7ccb82345
  Author: Gabriel Monnerat <gabriel@tiolive.com>
  Date:   Wed Aug 17 13:48:40 2016 +0000

      erp5_base: migrate scripts to new format

  commit 99d5d568d61169c51ec8586f1acf254f4bffc2e3
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Thu Jun 30 11:22:44 2016 +0200

      ERP5UserManager: Move to transactional cache for getPersonByReference method

  commit 0ea8f91666549a9783e7f4fb3ed7cfcb1fcfabab
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Wed Jan 20 17:32:17 2016 +0100

      ERP5SecurityManager: Use _person_cache in request to store path of user

  commit 5df794ed9fcfa4a61ee78c87aed1ac63c74c7d83
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Wed Jan 20 09:19:51 2016 +0000

      erp5_base: ERP5 Login reference can be edited. Login cannot be edited in validated state

  commit 4975b98e8c45fac2c526a6168ee7779decf29285
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Tue Jan 19 09:04:51 2016 +0000

      erp5_credential: Fix Update Credential Cookie on Credential Update for ERP5 Login

  commit 4022a8dfd8230eb1d6beebdda615238e39dd7412
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Mon Jan 18 16:59:53 2016 +0000

      erp5_credential: Do not accept Credential Update Synchronously when password is included

      The presence of the password in the Credential Update should not change the processing workflow
      of the Credential Update. For Now we just update the password and leave to rest to normal process

  commit 5320c5410a85e78c5449beeaed0912d53c42c36d
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Mon Jan 18 17:48:50 2016 +0100

      ERP5Security: fix getUserByLogin to work with ERP5 Login

  commit 999971e363a40af865ea31fe68278713e7e3714e
  Author: Cédric Le Ninivin <cedric.leninivin@tiolive.com>
  Date:   Mon Jan 18 16:44:45 2016 +0000

      erp5_credential: fix ERP5Site_newCredentialRecovery to use ERP5 Login

  commit cdc7d88a0f7d586cb46be6643a4f537ead0cb9fa
  Author: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
  Date:   Fri Dec 4 16:34:24 2015 +0100

      test: check if assignment change is effective immediately.

  commit 0851c138e6f91237a9c0653dba3f56eb92ff89dc
  Author: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
  Date:   Tue Dec 1 12:54:02 2015 +0000

      erp5_credential: support ERP5 Login.

  commit 823da06fa9312775deef59dbbed895fed061c4ed
  Author: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
  Date:   Wed Dec 2 15:24:26 2015 +0000

      erp5_core, erp5_base : move change password action and form from erp5_core to erp5_base.

  commit 9dc7ecbbca0a98b35adb559b7e6de42e530e2dc6
  Author: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
  Date:   Wed Nov 4 17:12:03 2015 +0100

      ERP5Security: Use a dedicated Login document to handle authentication.
parent 59f66814
<workflow_chain> <workflow_chain>
<chain> <chain>
<type>Person</type> <type>ERP5 Login</type>
<workflow>password_interaction_workflow</workflow> <workflow>password_interaction_workflow</workflow>
</chain> </chain>
</workflow_chain> </workflow_chain>
\ No newline at end of file
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_analyzePassword</string> </value> <value> <string>Login_analyzePassword</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_getListboxUrl</string> </value> <value> <string>Login_getListboxUrl</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_isLoginBlocked</string> </value> <value> <string>Login_isLoginBlocked</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_isPasswordExpired</string> </value> <value> <string>Login_isPasswordExpired</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -13,27 +13,13 @@ message_dict = { 0: 'Unknown error', ...@@ -13,27 +13,13 @@ message_dict = { 0: 'Unknown error',
-4: 'You have already used this password.', -4: 'You have already used this password.',
-5: 'You can not use any parts of your first and last name in password.'} -5: 'You can not use any parts of your first and last name in password.'}
def doValidation(person, password): def doValidation(login, password):
# raise so Formulator shows proper message # raise so Formulator shows proper message
result_code_list = person.Person_analyzePassword(password) result_code_list = login.analyzePassword(password)
if result_code_list!=[]: if result_code_list!=[]:
translateString = context.Base_translateString translateString = context.Base_translateString
message = ' '.join([translateString(message_dict[x]) for x in result_code_list]) message = ' '.join([translateString(message_dict[x]) for x in result_code_list])
raise ValidationError('external_validator_failed', context, error_text=message) raise ValidationError('external_validator_failed', context, error_text=message)
return 1 return 1
user_login = request.get('field_user_login', None) return doValidation(context, password)
# find Person object (or authenticated member) and validate it on it (password recovered for an existing account)
person = context.ERP5Site_getAuthenticatedMemberPersonValue(user_login)
if person is not None:
return doValidation(person, password)
# use a temp object (new account created)
first_name = request.get('field_your_first_name', None)
last_name = request.get('field_your_last_name', None)
kw = {'title': '%s %s' %(first_name, last_name),
'first_name': first_name,
'last_name': last_name}
person = newTempBase(portal, kw['title'], **kw)
return doValidation(person, password)
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Base_isPasswordValid</string> </value> <value> <string>Login_isPasswordValid</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_notifyLoginFailure</string> </value> <value> <string>Login_notifyLoginFailure</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_notifyPasswordExpire</string> </value> <value> <string>Login_notifyPasswordExpire</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_unblockLogin</string> </value> <value> <string>Login_unblockLogin</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -174,15 +174,15 @@ ...@@ -174,15 +174,15 @@
<list> <list>
<tuple> <tuple>
<string>title</string> <string>title</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
<tuple> <tuple>
<string>reference</string> <string>reference</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
<tuple> <tuple>
<string>count</string> <string>count</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<key> <string>after_script_name</string> </key> <key> <string>after_script_name</string> </key>
<value> <value>
<list> <list>
<string>Person_changePassword</string> <string>afterChangePassword</string>
</list> </list>
</value> </value>
</item> </item>
...@@ -72,10 +72,16 @@ ...@@ -72,10 +72,16 @@
<key> <string>portal_type_filter</string> </key> <key> <string>portal_type_filter</string> </key>
<value> <value>
<list> <list>
<string>Person</string> <string>ERP5 Login</string>
</list> </list>
</value> </value>
</item> </item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<none/>
</value>
</item>
<item> <item>
<key> <string>script_name</string> </key> <key> <string>script_name</string> </key>
<value> <value>
......
from DateTime import DateTime login = state_change['object']
portal = context.getPortalObject() portal = login.getPortalObject()
person = state_change['object']
# check preferences and save only if set # check preferences and save only if set
number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck() number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck()
if number_of_last_password_to_check is not None and number_of_last_password_to_check: if number_of_last_password_to_check is not None and number_of_last_password_to_check:
# save password and modification date # save password and modification date
current_password = person.getPassword() current_password = login.getPassword()
if current_password is not None: if current_password is not None:
password_event = portal.system_event_module.newContent(portal_type = 'Password Event', password_event = portal.system_event_module.newContent(portal_type='Password Event',
source_value = person, source_value=login,
destination_value = person, destination_value=login,
password = current_password) password=current_password)
password_event.confirm() password_event.confirm()
# Person_isPasswordExpired cache the wrong result if document is not in catalog. # Person_isPasswordExpired cache the wrong result if document is not in catalog.
# As the document is created in the same transaction, it is possible to force reindexation # As the document is created in the same transaction, it is possible to force reindexation
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_changePassword</string> </value> <value> <string>afterChangePassword</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
Person | password_interaction_workflow ERP5 Login | password_interaction_workflow
\ No newline at end of file \ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Login_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -6,6 +6,53 @@ ...@@ -6,6 +6,53 @@
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item> <item>
<key> <string>_count</string> </key> <key> <string>_count</string> </key>
<value> <value>
......
##############################################################################
#
# Copyright (c) 2015 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5.mixin.encrypted_password import EncryptedPasswordMixin
from Products.ERP5.mixin.login_account_provider import LoginAccountProviderMixin
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5Type.XMLObject import XMLObject
class Login(XMLObject, LoginAccountProviderMixin, EncryptedPasswordMixin):
meta_type = 'ERP5 Login'
portal_type = 'Login'
add_permission = Permissions.AddPortalContent
zope.interface.implements(interfaces.INode)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Reference
, PropertySheet.Login
, PropertySheet.LoginConstraint
)
def _setReference(self, value):
"""
Set the user id. This method is defined explicitly, because:
- we want to apply a different permission
- we want to prevent duplicated user ids, but only when
PAS _AND_ ERP5UserManager are used
"""
activate_kw = {}
portal = self.getPortalObject()
if value:
# Encode reference to hex to prevent uppercase/lowercase conflict in
# activity table (when calling countMessageWithTag)
activate_kw['tag'] = tag = \
self.getPortalType() + '_setReference_' + value.encode('hex')
# Check that there no existing user
erp5_users = portal.acl_users.erp5_users
login = erp5_users.getLoginObject(value, self.getPortalType())
if login is not None and login != self and \
login != self.getParentValue():
raise RuntimeError, 'user id %s already exist' % (value,)
# Check that there is no reindexation related to reference indexation
if portal.portal_activities.countMessageWithTag(tag):
raise RuntimeError, 'user id %s already exist' % (value,)
# Prevent concurrent transaction to set the same reference on 2
# different persons
self.getParentValue().getParentValue().serialize()
# Prevent to set the same reference on 2 different persons during the
# same transaction
transactional_variable = getTransactionalVariable()
if tag in transactional_variable:
raise RuntimeError, 'user id %s already exist' % (value,)
else:
transactional_variable[tag] = None
self._baseSetReference(value)
self.reindexObject(activate_kw=activate_kw)
# invalid the cache for ERP5Security
portal_caches = portal.portal_caches
portal_caches.clearCache(cache_factory_list=('erp5_content_short', ))
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Login</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.Login</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
def checkPersonLoginExistenceConsistency(self, fixit=False):
assert self.getPortalType() == 'Person'
reference = self.getReference()
if not reference:
# no login is required
return []
if not self.hasPassword():
# no login is required, but possibly another Login type object is required if implemented
return []
if len(self.objectValues(portal_type=self.getPortalObject().getPortalLoginTypeList())):
# already migrated
return []
if fixit:
login = self.newContent(
portal_type='ERP5 Login',
reference=reference,
)
login._setEncodedPassword(self.getPassword())
login.validate()
return ['%s has no Login type sub document.' % self.getRelativeUrl()]
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>PersonLoginMigration</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.PersonLoginMigration</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Extension Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
...@@ -109,6 +109,7 @@ ...@@ -109,6 +109,7 @@
<item>Career</item> <item>Career</item>
<item>Chat Address</item> <item>Chat Address</item>
<item>Credit Card</item> <item>Credit Card</item>
<item>ERP5 Login</item>
<item>Email</item> <item>Email</item>
<item>Embedded File</item> <item>Embedded File</item>
<item>Fax</item> <item>Fax</item>
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
</portal_type> </portal_type>
<portal_type id="Person"> <portal_type id="Person">
<item>DefaultImage</item> <item>DefaultImage</item>
<item>PersonUpgradeConstraint</item>
</portal_type> </portal_type>
<portal_type id="Preference"> <portal_type id="Preference">
<item>TelephonePreference</item> <item>TelephonePreference</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>content_icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>login</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5 Login</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>reference</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>Login</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -39,6 +39,10 @@ ...@@ -39,6 +39,10 @@
<type>Delivery Type</type> <type>Delivery Type</type>
<workflow>base_type_interaction_workflow, dynamic_class_generation_interaction_workflow</workflow> <workflow>base_type_interaction_workflow, dynamic_class_generation_interaction_workflow</workflow>
</chain> </chain>
<chain>
<type>ERP5 Login</type>
<workflow>edit_workflow, login_validation_workflow</workflow>
</chain>
<chain> <chain>
<type>Email</type> <type>Email</type>
<workflow>edit_workflow</workflow> <workflow>edit_workflow</workflow>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>LoginConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>not: here/Login_checkExistence</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>check_duplicate_constraint</string> </value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Error while checking the existence of the same User Login: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>The User Login is already used</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Existence Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>constraint_property</string> </key>
<value>
<tuple>
<string>reference</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>reference_existence_constraint</string> </value>
</item>
<item>
<key> <string>message_no_such_property</string> </key>
<value> <string>User Login must be defined</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Existence Constraint</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>PersonUpgradeConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Script Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>constraint_type/post_upgrade</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Login Existence_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Script Constraint</string> </value>
</item>
<item>
<key> <string>script_id</string> </key>
<value> <string>Person_checkPersonLoginExistenceConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, ComplexQuery
query = ComplexQuery(
SimpleQuery(portal_type=portal_type),
SimpleQuery(reference=reference),
)
login_list = [x for x in context.getPortalObject().portal_catalog(
select_list=('reference',),
query=query)
if x.reference == reference]
if ignore_uid is not None:
login_list = [x for x in login_list if x.uid != ignore_uid]
return len(login_list) > 0
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>portal_type, reference, ignore_uid=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_checkLoginExistence</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
person = context.ERP5Site_getAuthenticatedMemberPersonValue()
if person is None:
return []
else:
return [x.getReference() for x in person.objectValues(portal_type=portal_type) \
if x.getValidationState() == 'validated']
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>portal_type=\'ERP5 Login\'</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getValidatedLoginReferenceList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
<string>my_view_mode_telephone_number</string> <string>my_view_mode_telephone_number</string>
<string>my_view_mode_email_coordinate_text</string> <string>my_view_mode_email_coordinate_text</string>
<string>my_view_mode_email_url_string</string> <string>my_view_mode_email_url_string</string>
<string>my_view_mode_portal_type</string>
</list> </list>
</value> </value>
</item> </item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_view_mode_portal_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Type</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
result = []
sql_result = portal.ERP5Site_zGetDuplicateLoginReferenceList()
if len(sql_result):
from Products.CMFActivity.ActiveResult import ActiveResult
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, ComplexQuery
query = ComplexQuery(
*[ComplexQuery(SimpleQuery(portal_type=i['portal_type']),
SimpleQuery(reference=i['reference'])) for i in sql_result],
operator='OR')
active_result = ActiveResult()
for i in portal.portal_catalog(
select_list=('portal_type', 'reference',),
query=query):
result.append('%r, %r, %r' % (i['portal_type'], i['reference'], i['path']))
active_result.edit(summary='Logins having the same reference exist',
severity=len(sql_result),
detail='\n'.join(result))
active_process = context.newActiveProcess()
active_process.postResult(active_result)
return active_process
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_checkDuplicateLoginReferenceLogin</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
SELECT
reference, portal_type
FROM
catalog
WHERE
portal_type in (<dtml-in expr="portal_catalog.getPortalLoginTypeList()"><dtml-sqlvar sequence-item type="string"><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>)
AND validation_state='validated'
GROUP BY
reference, portal_type
HAVING
count(*) > 1
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_col</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>name</string> </key>
<value> <string>reference</string> </value>
</item>
<item>
<key> <string>null</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>t</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>15</int> </value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>name</string> </key>
<value> <string>portal_type</string> </value>
</item>
<item>
<key> <string>null</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>t</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>10</int> </value>
</item>
</dictionary>
</list>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_zGetDuplicateLoginReferenceList</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_portal_type</string>
<string>my_reference</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list>
<string>my_translated_validation_state_title</string>
</list>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ExternalLogin_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Login_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Login</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_portal_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_portal_type</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewBaseFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -10,14 +10,13 @@ ...@@ -10,14 +10,13 @@
<key> <string>delegated_list</string> </key> <key> <string>delegated_list</string> </key>
<value> <value>
<list> <list>
<string>default</string> <string>enabled</string>
<string>editable</string>
</list> </list>
</value> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>your_reference</string> </value> <value> <string>my_reference</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
...@@ -54,15 +53,11 @@ ...@@ -54,15 +53,11 @@
<value> <value>
<dictionary> <dictionary>
<item> <item>
<key> <string>default</string> </key> <key> <string>enabled</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -82,10 +77,6 @@ ...@@ -82,10 +77,6 @@
<key> <string>values</string> </key> <key> <string>values</string> </key>
<value> <value>
<dictionary> <dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <string>The username this person will use to log in the system.\n <value> <string>The username this person will use to log in the system.\n
...@@ -93,8 +84,8 @@ ...@@ -93,8 +84,8 @@
The system will check that there isn\'t another user with the same username.</string> </value> The system will check that there isn\'t another user with the same username.</string> </value>
</item> </item>
<item> <item>
<key> <string>editable</string> </key> <key> <string>enabled</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
...@@ -120,13 +111,19 @@ The system will check that there isn\'t another user with the same username.</st ...@@ -120,13 +111,19 @@ The system will check that there isn\'t another user with the same username.</st
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle> <pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/> <tuple>
<tuple>
<string>Products.Formulator.TALESField</string>
<string>TALESMethod</string>
</tuple>
<none/>
</tuple>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python: here.portal_membership.getAuthenticatedMember().getUserName()</string> </value> <value> <string>python: request.AUTHENTICATED_USER.has_permission(\'Manage users\', here)</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_translated_validation_state_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_translated_workflow_state_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
return context.getPortalObject().Base_checkLoginExistence(
portal_type=context.getPortalType(),
reference=context.getReference(),
ignore_uid=context.getUid(),
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Login_checkExistence</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_portal_type</string>
<string>my_reference</string>
<string>my_password</string>
<string>password_confirm</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list>
<string>my_translated_validation_state_title</string>
</list>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Login_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Login_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Login</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
<string>enabled</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_password</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>Password and confirmation doesn\'t match.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>enabled</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>The password of the user.\n
\n
Leaving this password empty will not modify existing password.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_password</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Person_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>User Password</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.Formulator.TALESField</string>
<string>TALESMethod</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: request.AUTHENTICATED_USER.has_permission(\'Set own password\', here)</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_portal_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_portal_type</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewBaseFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>enabled</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Person_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_translated_validation_state_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_translated_workflow_state_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
<string>default</string>
<string>enabled</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>password_confirm</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_password</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Person_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Password Confirmation</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.Formulator.TALESField</string>
<string>TALESMethod</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>string:</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<tuple>
<string>Products.Formulator.TALESField</string>
<string>TALESMethod</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: request.AUTHENTICATED_USER.has_permission(\'Set own password\', here)</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>checkPersonLoginExistenceConsistency</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>PersonLoginMigration</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_checkPersonLoginExistenceConsistency</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
error_list = []
if context.hasReference() and \
not len(context.objectValues(portal_type=context.getPortalObject().getPortalLoginTypeList())):
error_list.append('%s has no Login type sub document.' % context.getRelativeUrl())
if fixit:
context.Person_migrateToLogin()
return error_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>fixit=False, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_checkPreUpgradeLoginExistenceConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
def migrateLogin():
login = context.newContent(
portal_type='Login',
reference=context.getReference(),
password=context.getPassword(),
)
login.validate()
return login
login_list = context.objectValues(portal_type='Login')
if not login_list:
return [migrateLogin()]
elif not [x for x in login_list if x.getValidationState() == 'validated']:
return [migrateLogin()] + login_list
else:
return login_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>**kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getLoginValueList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>migrateToLogin</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>BaseMigration</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_migrateToLogin</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
<value> <value>
<list> <list>
<string>listbox</string> <string>listbox</string>
<string>login_listbox</string>
</list> </list>
</value> </value>
</item> </item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>portal_types</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>login_listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>Base_viewSearchResultList</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>all_editable_columns</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>reference</string>
<string>Login</string>
</tuple>
<tuple>
<string>translated_validation_state_title</string>
<string>State</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewBaseFieldLibrary</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>5</int> </value>
</item>
<item>
<key> <string>list_cookie</string> </key>
<value> <string>CONTACT_LIST</string> </value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list>
<tuple>
<string>ERP5 Login</string>
<string>ERP5 Login</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>reverse</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Login</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>40</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(x, x) for x in here.getPortalLoginTypeList()]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
request = context.REQUEST portal = context.getPortalObject()
new_password = request.get("new_password")
former_password = request.get("current_password")
user = getSecurityManager().getUser() user = getSecurityManager().getUser()
persons = context.acl_users.erp5_users.getUserByLogin(user) person = context.acl_users.erp5_users.getPersonByReference(user.getId())
person = persons[0] for login in person.objectValues(portal_type='ERP5 Login'):
if login.getReference() == reference and login.getValidationState() == 'validated':
if not person.checkPassword(former_password): break
msg = translateString("Current password is wrong.")
else: else:
msg = translateString("Password changed.") msg = translateString('You cannot change the password for %r.' % reference)
person.setPassword(new_password) return context.Base_redirect(dialog_id, keep_items=dict(portal_status_message=msg))
if not login.checkPassword(current_password):
msg = translateString("Current password is wrong.")
return context.Base_redirect(dialog_id, keep_items=dict(portal_status_message=msg))
login.setPassword(new_password)
# clear erp5_content_short cache (see _authenticateCredentials in Products.ERP5Security.ERP5UserManager) # clear erp5_content_short cache (see _authenticateCredentials in Products.ERP5Security.ERP5UserManager)
context.portal_caches.clearCache(('erp5_content_short',)) context.portal_caches.clearCache(('erp5_content_short',))
return context.logout() return portal.Base_redirect('logout')
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>form_id=\'view\', **kw</string> </value> <value> <string>new_password, current_password, reference, dialog_id=\'view\', **kw</string> </value>
</item> </item>
<item> <item>
<key> <string>_proxy_roles</string> </key> <key> <string>_proxy_roles</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>items</string>
<string>required</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_list_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>User Login</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(x, x) for x in here.Base_getValidatedLoginReferenceList()]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="DCWorkflowDefinition" module="Products.DCWorkflow.DCWorkflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>creation_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Validation helps confirming the entered data by relevant agents before it is shared.</string> </value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>login_validation_workflow</string> </value>
</item>
<item>
<key> <string>initial_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>manager_bypass</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Access contents information</string>
<string>Modify portal content</string>
<string>View</string>
<string>Add portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>state_var</string> </key>
<value> <string>validation_state</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validation Workflow</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Scripts" module="Products.DCWorkflow.Scripts"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>scripts</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>checkConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
obj = state_change['object']
obj.activate(tag='login:%s:%s' % (obj.getPortalType(), obj.getReference())).reindexObject()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>register_login</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
obj = state_change['object']
for plugin in obj.getPortalObject().acl_users.objectValues():
if getattr(plugin, 'getLoginPortalType', lambda: None)() == obj.getPortalType():
plugin.unregisterLogin(obj)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>unregister_login</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="States" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>states</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StateDefinition" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Documents in this state were deleted by the user as a result of clicking on the trash button or calling the delete action</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>deleted</string> </value>
</item>
<item>
<key> <string>permission_roles</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Deleted</string> </value>
</item>
<item>
<key> <string>transitions</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_list</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Persistence</string>
<string>PersistentMapping</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_container</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StateDefinition" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Default state of the document</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>permission_roles</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Draft</string> </value>
</item>
<item>
<key> <string>transitions</string> </key>
<value>
<tuple>
<string>delete</string>
<string>delete_action</string>
<string>validate</string>
<string>validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_list</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Persistence</string>
<string>PersistentMapping</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_container</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StateDefinition" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>State of a document that has been invalidated in ERP5</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>invalidated</string> </value>
</item>
<item>
<key> <string>permission_roles</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Invalidated</string> </value>
</item>
<item>
<key> <string>transitions</string> </key>
<value>
<tuple>
<string>delete</string>
<string>delete_action</string>
<string>validate</string>
<string>validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_list</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Persistence</string>
<string>PersistentMapping</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_container</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StateDefinition" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>State of a document that has been validated in ERP5</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>validated</string> </value>
</item>
<item>
<key> <string>permission_roles</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validated</string> </value>
</item>
<item>
<key> <string>transitions</string> </key>
<value>
<tuple>
<string>invalidate</string>
<string>invalidate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_list</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Transitions" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transitions</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Delete a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>delete</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string>deleted</string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Delete</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string>delete</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Delete a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>delete_action</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Delete Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>roles</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>This action invalidates a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>invalidate</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string>invalidated</string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Invalidate</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string>Invalidate</string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=invalidate_action</string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string>invalidate</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>This action invalidates a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>invalidate_action</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Invalidate Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>roles</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Validates a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string>validated</string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string>register_login</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validate</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string>Validate</string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=validate_action</string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Validates a document in ERP5</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>validate_action</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string>checkConsistency</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validate Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>roles</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Variables" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variables</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>action</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.CMFCore.Expression</string>
<string>Expression</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>transition/getId|nothing</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>actor</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.CMFCore.Expression</string>
<string>Expression</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>user/getUserName</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>comment</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.CMFCore.Expression</string>
<string>Expression</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python:state_change.kwargs.get(\'comment\',\'\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>error_message</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>history</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.CMFCore.Expression</string>
<string>Expression</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>state_change/getHistory</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>portal_type</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>time</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<tuple>
<string>Products.CMFCore.Expression</string>
<string>Expression</string>
</tuple>
<none/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>state_change/getDateTime</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Worklists" module="Products.DCWorkflow.Worklists"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>worklists</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -32,6 +32,7 @@ Delivery Type | role_view ...@@ -32,6 +32,7 @@ Delivery Type | role_view
Delivery Type | translation_view Delivery Type | translation_view
Delivery Type | update_local_roles Delivery Type | update_local_roles
Delivery Type | view Delivery Type | view
ERP5 Login | view
Email | change_function Email | change_function
Email | view Email | view
Embedded File | download Embedded File | download
...@@ -87,6 +88,7 @@ Person | person_detailed_report ...@@ -87,6 +88,7 @@ Person | person_detailed_report
Person | terminate_career Person | terminate_career
Person | view Person | view
Preference | base_preference Preference | base_preference
Preference | change_password
Previous Causality Movement Group | view Previous Causality Movement Group | view
Property Assignment Movement Group | view Property Assignment Movement Group | view
Property Grouping Movement Group | view Property Grouping Movement Group | view
...@@ -108,4 +110,4 @@ Variant Movement Group | view ...@@ -108,4 +110,4 @@ Variant Movement Group | view
Variation Property Movement Group | view Variation Property Movement Group | view
portal_actions | jump_query portal_actions | jump_query
portal_actions | jump_related_object portal_actions | jump_related_object
portal_actions | post_query portal_actions | post_query
\ No newline at end of file
document.erp5.GeographicalArea document.erp5.GeographicalArea
document.erp5.GeographicalPoint document.erp5.GeographicalPoint
\ No newline at end of file document.erp5.Login
\ No newline at end of file
extension.erp5.BarcodeUtils
extension.erp5.BaseMigration extension.erp5.BaseMigration
extension.erp5.BarcodeUtils extension.erp5.PersonLoginMigration
\ No newline at end of file \ No newline at end of file
...@@ -76,6 +76,7 @@ Person | Bank Account ...@@ -76,6 +76,7 @@ Person | Bank Account
Person | Career Person | Career
Person | Chat Address Person | Chat Address
Person | Credit Card Person | Credit Card
Person | ERP5 Login
Person | Email Person | Email
Person | Embedded File Person | Embedded File
Person | Fax Person | Fax
......
...@@ -19,6 +19,7 @@ Delivery Builder ...@@ -19,6 +19,7 @@ Delivery Builder
Delivery Causality Assignment Movement Group Delivery Causality Assignment Movement Group
Delivery Tool Delivery Tool
Delivery Type Delivery Type
ERP5 Login
Email Email
Embedded File Embedded File
Embedded Folder Embedded Folder
...@@ -62,4 +63,4 @@ Split Movement Group ...@@ -62,4 +63,4 @@ Split Movement Group
Telephone Telephone
Title Movement Group Title Movement Group
Variant Movement Group Variant Movement Group
Variation Property Movement Group Variation Property Movement Group
\ No newline at end of file
...@@ -2,6 +2,7 @@ Notification Message | ItemAggregation ...@@ -2,6 +2,7 @@ Notification Message | ItemAggregation
Notification Message | Reference Notification Message | Reference
Organisation | DefaultImage Organisation | DefaultImage
Person | DefaultImage Person | DefaultImage
Person | PersonUpgradeConstraint
Preference | TelephonePreference Preference | TelephonePreference
Previous Causality Movement Group | PreviousCausalityMovementGroup Previous Causality Movement Group | PreviousCausalityMovementGroup
Query | TextDocument Query | TextDocument
\ No newline at end of file
...@@ -17,6 +17,8 @@ Currency | edit_workflow ...@@ -17,6 +17,8 @@ Currency | edit_workflow
Currency | validation_workflow Currency | validation_workflow
Delivery Type | base_type_interaction_workflow Delivery Type | base_type_interaction_workflow
Delivery Type | dynamic_class_generation_interaction_workflow Delivery Type | dynamic_class_generation_interaction_workflow
ERP5 Login | edit_workflow
ERP5 Login | login_validation_workflow
Email | edit_workflow Email | edit_workflow
Embedded File | document_conversion_interaction_workflow Embedded File | document_conversion_interaction_workflow
Embedded File | edit_workflow Embedded File | edit_workflow
...@@ -50,4 +52,4 @@ Role Definition | edit_workflow ...@@ -50,4 +52,4 @@ Role Definition | edit_workflow
Role Definition | local_permission_interaction_workflow Role Definition | local_permission_interaction_workflow
Rounding Model | validation_workflow Rounding Model | validation_workflow
Simulation Movement | simulation_movement_causality_interaction_workflow Simulation Movement | simulation_movement_causality_interaction_workflow
Telephone | edit_workflow Telephone | edit_workflow
\ No newline at end of file
LoginConstraint
PersonUpgradeConstraint
PreviousCausalityMovementGroup PreviousCausalityMovementGroup
\ No newline at end of file
...@@ -10,6 +10,7 @@ document_conversion_interaction_workflow ...@@ -10,6 +10,7 @@ document_conversion_interaction_workflow
document_security_interaction_workflow document_security_interaction_workflow
embedded_workflow embedded_workflow
local_permission_interaction_workflow local_permission_interaction_workflow
login_validation_workflow
movement_resource_interaction_workflow movement_resource_interaction_workflow
notification_message_workflow notification_message_workflow
person_interaction_workflow person_interaction_workflow
......
...@@ -225,7 +225,6 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin): ...@@ -225,7 +225,6 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin):
person.getFirstName()) person.getFirstName())
self.assertEqual(user_info["field_your_last_name"], self.assertEqual(user_info["field_your_last_name"],
person.getLastName()) person.getLastName())
self.assertNotEquals(person.getPassword(), None)
self.assertEqual(user_info["field_your_function"], self.assertEqual(user_info["field_your_function"],
person.getFunction()) person.getFunction())
self.assertEqual(user_info["field_your_default_email_text"], self.assertEqual(user_info["field_your_default_email_text"],
...@@ -236,6 +235,9 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin): ...@@ -236,6 +235,9 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin):
assignment_list = person.contentValues(portal_type='Assignment') assignment_list = person.contentValues(portal_type='Assignment')
self.assertEqual(len(assignment_list), 1) self.assertEqual(len(assignment_list), 1)
self.assertEqual('my_group', assignment_list[0].getGroup()) self.assertEqual('my_group', assignment_list[0].getGroup())
login_list = person.contentValues(portal_type='ERP5 Login')
self.assertEqual(len(login_list), 1)
self.assertNotEquals(login_list[0].getPassword(), None)
def stepCheckSocialTitleCategory(self, sequence=None,sequence_list=None, **kw): def stepCheckSocialTitleCategory(self, sequence=None,sequence_list=None, **kw):
"""Check that the social title category is configured. """Check that the social title category is configured.
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>default_email_text=None, last_name=None, first_name=None, gender=None, nationality=None, password=None, date_of_birth=None, default_telephone_telephone_country=None, default_telephone_text=None, default_mobile_telephone_telephone_country=None, default_mobile_telephone_text=None, default_fax_text=None, default_credential_question_question=None, default_credential_question_question_free_text=None, default_credential_question_answer=None, function=None, activity_list=None, skill_list=None, default_address_city=None, default_address_street_address=None, default_address_zip_code=None,default_address_region=None,default_image_file=None, description=None, dialog_id=\'\', **kw</string> </value> <value> <string>default_email_text=None, last_name=None, first_name=None, gender=None, nationality=None, password=None, date_of_birth=None, default_telephone_telephone_country=None, default_telephone_text=None, default_mobile_telephone_telephone_country=None, default_mobile_telephone_text=None, default_fax_text=None, default_credential_question_question=None, default_credential_question_question_free_text=None, default_credential_question_answer=None, function=None, activity_list=None, skill_list=None, default_address_city=None, default_address_street_address=None, default_address_zip_code=None,default_address_region=None,default_image_file=None, description=None, reference=None, dialog_id=\'\', **kw</string> </value>
</item> </item>
<item> <item>
<key> <string>_proxy_roles</string> </key> <key> <string>_proxy_roles</string> </key>
......
...@@ -93,6 +93,7 @@ ...@@ -93,6 +93,7 @@
<key> <string>left</string> </key> <key> <string>left</string> </key>
<value> <value>
<list> <list>
<string>your_reference</string>
<string>your_gender</string> <string>your_gender</string>
<string>your_first_name</string> <string>your_first_name</string>
<string>your_last_name</string> <string>your_last_name</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>items</string>
<string>required</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_list_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>User Login</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>here/Base_getValidatedLoginReferenceList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<value> <value>
<list> <list>
<tuple> <tuple>
<string>Person</string> <string>ERP5 Login</string>
<string>Person</string> <string>ERP5 Login</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<value> <value>
<list> <list>
<tuple> <tuple>
<string>Person</string> <string>ERP5 Login</string>
<string>Person</string> <string>ERP5 Login</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -1536,6 +1536,14 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -1536,6 +1536,14 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
return self._getPortalGroupedTypeList('entity') or\ return self._getPortalGroupedTypeList('entity') or\
self._getPortalConfiguration('portal_entity_type_list') self._getPortalConfiguration('portal_entity_type_list')
security.declareProtected(Permissions.AccessContentsInformation,
'getPortalLoginTypeList')
def getPortalLoginTypeList(self):
"""
Returns Login types.
"""
return self._getPortalGroupedTypeList('login')
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getDefaultModuleId') 'getDefaultModuleId')
def getDefaultModuleId(self, portal_type, default=MARKER, only_visible=False): def getDefaultModuleId(self, portal_type, default=MARKER, only_visible=False):
......
...@@ -272,11 +272,10 @@ class PasswordTool(BaseTool): ...@@ -272,11 +272,10 @@ class PasswordTool(BaseTool):
# XXX: incorrect grammar # XXX: incorrect grammar
return error("Date has expire.") return error("Date has expire.")
del self._password_request_dict[password_key] del self._password_request_dict[password_key]
persons = self.getPortalObject().acl_users.erp5_users.getUserByLogin( login = self.getPortalObject().acl_users.erp5_users.getLoginObject(
register_user_login) register_user_login, 'ERP5 Login')
person = persons[0] login._forceSetPassword(password)
person._forceSetPassword(password) login.reindexObject()
person.reindexObject()
return redirect(REQUEST, site_url, return redirect(REQUEST, site_url,
translateString("Password changed.")) translateString("Password changed."))
......
...@@ -53,18 +53,12 @@ def getSecurityCategoryFromAssignment(self, base_category_list, user_name, objec ...@@ -53,18 +53,12 @@ def getSecurityCategoryFromAssignment(self, base_category_list, user_name, objec
category_list = [] category_list = []
person_object_list = self.portal_catalog.unrestrictedSearchResults( person_object = self.getPortalObject().acl_users.erp5_users.getPersonByReference(user_name)
query=SimpleQuery(reference=user_name), portal_type='Person') if person_object is None:
# if a person_object was not found in the module, we do nothing more
if len(person_object_list) != 1: # this happens for example when a manager with no associated person object
if len(person_object_list) > 1: # creates a person_object for a new user
raise ConsistencyError, "Error: There is more than one Person with reference '%s'" % user_name return []
else:
# if a person_object was not found in the module, we do nothing more
# this happens for example when a manager with no associated person object
# creates a person_object for a new user
return []
person_object = person_object_list[0].getObject()
# We look for every valid assignments of this user # We look for every valid assignments of this user
for assignment in person_object.contentValues(filter={'portal_type': 'Assignment'}): for assignment in person_object.contentValues(filter={'portal_type': 'Assignment'}):
......
person = context.ERP5Site_getAuthenticatedMemberPersonValue()
if person is None:
return []
else:
return [(x.getReference(), x.getRelativeUrl()) for x in \
person.objectValues(portal_type='ERP5 Login') \
if x.getValidationState() == 'validated']
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getValidatedLoginItemList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -23,7 +23,6 @@ Attribute Unicity Constraint | view ...@@ -23,7 +23,6 @@ Attribute Unicity Constraint | view
Base Category | view Base Category | view
Base Domain | view Base Domain | view
Base Type | action_view Base Type | action_view
Base Type | jump_property_sheets
Base Type | role_view Base Type | role_view
Base Type | translation_view Base Type | translation_view
Base Type | update_local_roles Base Type | update_local_roles
...@@ -80,17 +79,14 @@ Id Tool | view ...@@ -80,17 +79,14 @@ Id Tool | view
Memcached Plugin | view Memcached Plugin | view
Memcached Tool | view Memcached Tool | view
Predicate | view Predicate | view
Preference Tool Type | jump_property_sheets
Preference Tool Type | view Preference Tool Type | view
Preference Tool | advanced Preference Tool | advanced
Preference Tool | view Preference Tool | view
Preference Type | action_view Preference Type | action_view
Preference Type | jump_property_sheets
Preference Type | role_view Preference Type | role_view
Preference Type | translation_view Preference Type | translation_view
Preference Type | update_local_roles Preference Type | update_local_roles
Preference Type | view Preference Type | view
Preference | change_password
Preference | html_style_preferences Preference | html_style_preferences
Preference | view Preference | view
Preference | view_template Preference | view_template
......
...@@ -66,10 +66,12 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -66,10 +66,12 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
reference = 'test') reference = 'test')
if portal.portal_catalog.getResultValue(**kw) is None: if portal.portal_catalog.getResultValue(**kw) is None:
# add a loggable Person # add a loggable Person
person = portal.person_module.newContent(password = 'test', person = self.createUser(
first_name = 'First', kw['reference'],
last_name = 'Last', password='test',
**kw) person_kw={'first_name': 'First',
'last_name': 'Last'},
)
person.validate() person.validate()
assignment = person.newContent(portal_type = 'Assignment') assignment = person.newContent(portal_type = 'Assignment')
assignment.open() assignment.open()
...@@ -91,16 +93,33 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -91,16 +93,33 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
'erp5_content_short', # for authentication cache 'erp5_content_short', # for authentication cache
)) ))
def _getPasswordEventList(self, person): def _getPasswordEventList(self, login):
return [x.getObject() for x in self.portal.portal_catalog( return [x.getObject() for x in self.portal.portal_catalog(
portal_type = 'Password Event', portal_type = 'Password Event',
default_destination_uid = person.getUid(), default_destination_uid = login.getUid(),
sort_on = (('creation_date', 'DESC',),))] sort_on = (('creation_date', 'DESC',),))]
def _cleanUpPerson(self, person): def _cleanUpLogin(self, login):
self.portal.system_event_module.manage_delObjects([x.getId() for x in self._getPasswordEventList(person)]) self.portal.system_event_module.manage_delObjects([x.getId() for x in self._getPasswordEventList(login)])
def createUser(self, reference, password=None, person_kw=None):
"""
Modified version from ERP5TypeTestCase, that does set reference as
password when password is None.
"""
if person_kw is None:
person_kw = {}
person = self.portal.person_module.newContent(portal_type='Person',
reference=reference,
**person_kw)
login = person.newContent(portal_type='ERP5 Login',
reference=reference,
password=password)
login.validate()
return person
def test_01_BlockLogin(self): def test_01_BlockLogin(self):
""" """
Test that a recataloging works for Web Site documents Test that a recataloging works for Web Site documents
...@@ -110,73 +129,74 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -110,73 +129,74 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = portal.portal_catalog.getResultValue(portal_type = 'Person',
reference = 'test') reference = 'test')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
# login should be allowed # login should be allowed
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# file some failures so we should detect and block account # file some failures so we should detect and block account
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.tic() self.tic()
# should be blocked # should be blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set check back interval to actualy disable blocking # set check back interval to actualy disable blocking
preference.setPreferredAuthenticationFailureCheckDuration(0) preference.setPreferredAuthenticationFailureCheckDuration(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
time.sleep(1) # we need to give a moment time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# .. and revert it back # .. and revert it back
preference.setPreferredAuthenticationFailureCheckDuration(600) preference.setPreferredAuthenticationFailureCheckDuration(600)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# increase failures attempts # increase failures attempts
preference.setPreferredMaxAuthenticationFailure(4) preference.setPreferredMaxAuthenticationFailure(4)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# .. and revert it back # .. and revert it back
preference.setPreferredMaxAuthenticationFailure(3) preference.setPreferredMaxAuthenticationFailure(3)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set short block interval so we can test it as well # set short block interval so we can test it as well
preference.setPreferredAuthenticationFailureBlockDuration(3) preference.setPreferredAuthenticationFailureBlockDuration(3)
self._clearCache() self._clearCache()
self.tic() self.tic()
time.sleep(4) time.sleep(4)
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# test multiple concurrent transactions without waiting for activities to be over # test multiple concurrent transactions without waiting for activities to be over
preference.setPreferredAuthenticationFailureCheckDuration(600) preference.setPreferredAuthenticationFailureCheckDuration(600)
preference.setPreferredAuthenticationFailureBlockDuration(600) preference.setPreferredAuthenticationFailureBlockDuration(600)
preference.setPreferredMaxAuthenticationFailure(3) preference.setPreferredMaxAuthenticationFailure(3)
person.Person_unblockLogin() login.Login_unblockLogin()
self._clearCache() self._clearCache()
self.tic() self.tic()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.commit() self.commit()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# test unblock account # test unblock account
person.Person_unblockLogin() login.Login_unblockLogin()
self.tic() self.tic()
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
def test_02_PasswordHistory(self): def test_02_PasswordHistory(self):
...@@ -186,61 +206,61 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -186,61 +206,61 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
portal = self.getPortal() portal = self.getPortal()
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser('test-02')
reference = 'test-02') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
self.tic() self.tic()
# Check that last (X where X is set in preferences) passwords are saved. # Check that last (X where X is set in preferences) passwords are saved.
self.assertEqual([], self._getPasswordEventList(person)) self.assertEqual([], self._getPasswordEventList(login))
preference.setPreferredNumberOfLastPasswordToCheck(10) preference.setPreferredNumberOfLastPasswordToCheck(10)
self.tic() self.tic()
self._clearCache() self._clearCache()
person.setPassword('12345678') login.setPassword('12345678')
self.tic() self.tic()
# password change date should be saved as well hashed old password value # password change date should be saved as well hashed old password value
old_password = person.getPassword() old_password = login.getPassword()
self.assertSameSet([old_password], [x.getPassword() for x in self._getPasswordEventList(person)]) self.assertSameSet([old_password], [x.getPassword() for x in self._getPasswordEventList(login)])
# .. test one more time to check history of password is saved in a list # .. test one more time to check history of password is saved in a list
person.setPassword('123456789') login.setPassword('123456789')
self.tic() self.tic()
old_password1 = person.getPassword() old_password1 = login.getPassword()
# password change date should be saved as well hashed old password value # password change date should be saved as well hashed old password value
self.assertSameSet([old_password1, old_password], [x.getPassword() for x in self._getPasswordEventList(person)]) self.assertSameSet([old_password1, old_password], [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (_setPassword)... # other methods (_setPassword)...
person._setPassword('123456789-1') login._setPassword('123456789-1')
self.tic() self.tic()
old_password2 = person.getPassword() old_password2 = login.getPassword()
self.assertSameSet([old_password2, old_password1, old_password], \ self.assertSameSet([old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (_forceSetPassword)... # other methods (_forceSetPassword)...
person._forceSetPassword('123456789-2') login._forceSetPassword('123456789-2')
self.tic() self.tic()
old_password3 = person.getPassword() old_password3 = login.getPassword()
self.assertSameSet([old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (setEncodedPassword)... # other methods (setEncodedPassword)...
person.setEncodedPassword('123456789-3') login.setEncodedPassword('123456789-3')
self.tic() self.tic()
old_password4 = person.getPassword() old_password4 = login.getPassword()
self.assertSameSet([old_password4, old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password4, old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (edit)... # other methods (edit)...
person.edit(password = '123456789-4') login.edit(password = '123456789-4')
self.tic() self.tic()
old_password5 = person.getPassword() old_password5 = login.getPassword()
self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
def test_03_PasswordValidity(self): def test_03_PasswordValidity(self):
...@@ -258,105 +278,107 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -258,105 +278,107 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser(
reference = 'test-03', 'test-03',
password = 'test', password='test',
first_name = 'First', person_kw={'first_name': 'First',
last_name = 'Last') 'last_name': 'Last'},
)
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
self.tic() self.tic()
# by default an empty password if nothing set in preferences is OK # by default an empty password if nothing set in preferences is OK
self.assertTrue(person.isPasswordValid('')) self.assertTrue(login.isPasswordValid(''))
# Not long enough passwords used # Not long enough passwords used
self._cleanUpPerson(person) self._cleanUpLogin(login)
preference.setPreferredMinPasswordLength(8) preference.setPreferredMinPasswordLength(8)
preference.setPreferredNumberOfLastPasswordToCheck(0) preference.setPreferredNumberOfLastPasswordToCheck(0)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertEqual([-1], person.analyzePassword('')) self.assertEqual([-1], login.analyzePassword(''))
self.assertEqual([-1], person.analyzePassword('1234567')) self.assertEqual([-1], login.analyzePassword('1234567'))
self.assertTrue(person.isPasswordValid('12345678')) self.assertTrue(login.isPasswordValid('12345678'))
# not changed in last x days # not changed in last x days
self._cleanUpPerson(person) self._cleanUpLogin(login)
preference.setPreferredMinPasswordLifetimeDuration(24) preference.setPreferredMinPasswordLifetimeDuration(24)
preference.setPreferredNumberOfLastPasswordToCheck(3) preference.setPreferredNumberOfLastPasswordToCheck(3)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertTrue(person.isPasswordValid('12345678')) self.assertTrue(login.isPasswordValid('12345678'))
person.setPassword('12345678') login.setPassword('12345678')
self.tic() self.tic()
# if we try to change now we should fail with any password # if we try to change now we should fail with any password
self.assertSameSet([-3], person.analyzePassword('87654321')) self.assertSameSet([-3], login.analyzePassword('87654321'))
self.assertSameSet([-1, -3], person.analyzePassword('short')) # multiple failures self.assertSameSet([-1, -3], login.analyzePassword('short')) # multiple failures
self.assertFalse(person.isPasswordValid('short')) # multiple failures self.assertFalse(login.isPasswordValid('short')) # multiple failures
self.assertRaises(ValueError, person.setPassword, '87654321') self.assertRaises(ValueError, login.setPassword, '87654321')
preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertTrue(person.isPasswordValid('87654321')) # it's OK to change self.assertTrue(login.isPasswordValid('87654321')) # it's OK to change
# password not used in previous X passwords # password not used in previous X passwords
preference.setPreferredMinPasswordLength(None) # disable for now preference.setPreferredMinPasswordLength(None) # disable for now
self._cleanUpPerson(person) self._cleanUpLogin(login)
self._clearCache() self._clearCache()
self.tic() self.tic()
person.setPassword('12345678-new') login.setPassword('12345678-new')
self.tic() self.tic()
self.assertSameSet([-4], person.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password self.assertSameSet([-4], login.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password
self.assertRaises(ValueError, person.setPassword, '12345678-new') self.assertRaises(ValueError, login.setPassword, '12345678-new')
self.assertTrue(person.isPasswordValid('12345678_')) # it's OK with another one not used yet self.assertTrue(login.isPasswordValid('12345678_')) # it's OK with another one not used yet
for password in ['a','b','c','d', 'e', 'f']: for password in ['a','b','c','d', 'e', 'f']:
# this sleep is not so beautiful, but mysql datetime columns has a # this sleep is not so beautiful, but mysql datetime columns has a
# precision of one second only, and we use creation_date to order # precision of one second only, and we use creation_date to order
# "Password Event" objects. So without this sleep, the test is # "Password Event" objects. So without this sleep, the test is
# failing randomly. # failing randomly.
time.sleep(1) time.sleep(1)
person.setPassword(password) login.setPassword(password)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('12345678-new')) self.assertTrue(login.isPasswordValid('12345678-new'))
self.assertTrue(person.isPasswordValid('a')) self.assertTrue(login.isPasswordValid('a'))
self.assertTrue(person.isPasswordValid('b')) self.assertTrue(login.isPasswordValid('b'))
self.assertTrue(person.isPasswordValid('c')) self.assertTrue(login.isPasswordValid('c'))
# only last 3 (including current one are invalid) # only last 3 (including current one are invalid)
self.assertSameSet([-4], person.analyzePassword('d')) self.assertSameSet([-4], login.analyzePassword('d'))
self.assertSameSet([-4], person.analyzePassword('e')) self.assertSameSet([-4], login.analyzePassword('e'))
self.assertSameSet([-4], person.analyzePassword('f')) self.assertSameSet([-4], login.analyzePassword('f'))
# if we remove restricted then all password are usable # if we remove restricted then all password are usable
preference.setPreferredNumberOfLastPasswordToCheck(None) preference.setPreferredNumberOfLastPasswordToCheck(None)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('d')) self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e')) self.assertTrue(login.isPasswordValid('e'))
self.assertTrue(person.isPasswordValid('f')) self.assertTrue(login.isPasswordValid('f'))
# if we set only last password to check # if we set only last password to check
preference.setPreferredNumberOfLastPasswordToCheck(1) preference.setPreferredNumberOfLastPasswordToCheck(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('c')) self.assertTrue(login.isPasswordValid('c'))
self.assertTrue(person.isPasswordValid('d')) self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e')) self.assertTrue(login.isPasswordValid('e'))
self.assertSameSet([-4], person.analyzePassword('f')) self.assertSameSet([-4], login.analyzePassword('f'))
preference.setPreferredRegularExpressionGroupList(regular_expression_list) preference.setPreferredRegularExpressionGroupList(regular_expression_list)
preference.setPreferredMinPasswordLength(7) preference.setPreferredMinPasswordLength(7)
preference.setPreferredNumberOfLastPasswordToCheck(None) preference.setPreferredNumberOfLastPasswordToCheck(None)
self._cleanUpPerson(person) self._cleanUpLogin(login)
self._clearCache() self._clearCache()
self.tic() self.tic()
...@@ -370,47 +392,47 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -370,47 +392,47 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list: for password in four_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.isPasswordValid(password))
for password in three_group_password_list+two_group_password_list + one_group_password_list: for password in three_group_password_list+two_group_password_list + one_group_password_list:
self.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 3 out of all groups # min 3 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(3) preference.setPreferredMinRegularExpressionGroupNumber(3)
self._clearCache() self._clearCache()
self._cleanUpPerson(person) self._cleanUpLogin(login)
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list: for password in four_group_password_list + three_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.isPasswordValid(password))
for password in two_group_password_list + one_group_password_list: for password in two_group_password_list + one_group_password_list:
self.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 2 out of all groups # min 2 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(2) preference.setPreferredMinRegularExpressionGroupNumber(2)
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list + two_group_password_list: for password in four_group_password_list + three_group_password_list + two_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.isPasswordValid(password))
for password in one_group_password_list: for password in one_group_password_list:
self.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 1 out of all groups # min 1 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(1) preference.setPreferredMinRegularExpressionGroupNumber(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list + two_group_password_list+one_group_password_list: for password in four_group_password_list + three_group_password_list + two_group_password_list+one_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.isPasswordValid(password))
# not contain the full name of the user # not contain the full name of the user
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertSameSet([-5], person.analyzePassword('abAB#12_%s' %person.getFirstName())) self.assertSameSet([-5], login.analyzePassword('abAB#12_%s' %person.getFirstName()))
self.assertSameSet([-5], person.analyzePassword('abAB#12_%s' %person.getLastName())) self.assertSameSet([-5], login.analyzePassword('abAB#12_%s' %person.getLastName()))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getFirstName())) self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getFirstName()))
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getLastName())) self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getLastName()))
# check on temp objects just passworrd length( i.e. simulating a new user account creation) # check on temp objects just passworrd length( i.e. simulating a new user account creation)
first_name = 'John' first_name = 'John'
...@@ -425,8 +447,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -425,8 +447,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
# in this case which is basically used in new account creation only length of password matters # in this case which is basically used in new account creation only length of password matters
self.assertSameSet([-1], temp_person.Person_analyzePassword('onlyNine1')) self.assertSameSet([-1], temp_person.Login_analyzePassword('onlyNine1'))
self.assertSameSet([], temp_person.Person_analyzePassword('longEnough1')) self.assertSameSet([], temp_person.Login_analyzePassword('longEnough1'))
# make sure re check works on temp as well ( i.e. min 3 out of all groups) # make sure re check works on temp as well ( i.e. min 3 out of all groups)
preference.setPreferredRegularExpressionGroupList(regular_expression_list) preference.setPreferredRegularExpressionGroupList(regular_expression_list)
...@@ -435,22 +457,22 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -435,22 +457,22 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list: for password in four_group_password_list + three_group_password_list:
self.assertSameSet([], temp_person.Person_analyzePassword(password)) self.assertSameSet([], temp_person.Login_analyzePassword(password))
for password in two_group_password_list + one_group_password_list: for password in two_group_password_list + one_group_password_list:
self.assertSameSet([-2], temp_person.Person_analyzePassword(password)) self.assertSameSet([-2], temp_person.Login_analyzePassword(password))
# make sure peron's check on username works on temp as well (i.e. not contain the full name of the user) # make sure peron's check on username works on temp as well (i.e. not contain the full name of the user)
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertSameSet([-5], temp_person.Person_analyzePassword('abAB#12_%s' %first_name)) self.assertSameSet([-5], temp_person.Login_analyzePassword('abAB#12_%s' %first_name))
self.assertSameSet([-5], temp_person.Person_analyzePassword('abAB#12_%s' %last_name)) self.assertSameSet([-5], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertSameSet([], temp_person.Person_analyzePassword('abAB#12_%s' %first_name)) self.assertSameSet([], temp_person.Login_analyzePassword('abAB#12_%s' %first_name))
self.assertSameSet([], temp_person.Person_analyzePassword('abAB#12_%s' %last_name)) self.assertSameSet([], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
# check Base_isPasswordValid is able to work in Anonymous User fashion # check Base_isPasswordValid is able to work in Anonymous User fashion
# but with already create Person object (i.e. recover password case) # but with already create Person object (i.e. recover password case)
...@@ -461,20 +483,20 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -461,20 +483,20 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
person.setPassword('used_ALREADY_1234') login.setPassword('used_ALREADY_1234')
self._clearCache() self._clearCache()
self.tic() self.tic()
# emulate Anonymous User # emulate Anonymous User
self.logout() self.logout()
request.set('field_user_login', person.getReference()) request.set('field_user_login', login.getReference())
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#12_%s' %person.getFirstName(), request) # contains name self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getFirstName(), request) # contains name
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#12_%s' %person.getLastName(), request) # contains name self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getLastName(), request) # contains name
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#1', request) # too short self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#1', request) # too short
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abABCDEFG', request) # too few groups self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abABCDEFG', request) # too few groups
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'used_ALREADY_1234', request) # already used self.assertRaises(ValidationError, login.Login_isPasswordValid, 'used_ALREADY_1234', request) # already used
self.assertEqual(1, portal.Base_isPasswordValid('abAB#12_', request)) self.assertEqual(1, login.Login_isPasswordValid('abAB#12_', request))
self.assertEqual(1, portal.Base_isPasswordValid('not_used_ALREADY_1234', request)) self.assertEqual(1, login.Login_isPasswordValid('not_used_ALREADY_1234', request))
def test_04_PasswordExpire(self): def test_04_PasswordExpire(self):
""" """
...@@ -485,16 +507,16 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -485,16 +507,16 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser('test-04',
reference = 'test-04', password='used_ALREADY_1234')
password = 'used_ALREADY_1234') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
preference.setPreferredMaxPasswordLifetimeDuration(24) preference.setPreferredMaxPasswordLifetimeDuration(24)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired']) self.assertFalse(request['is_user_account_password_expired'])
...@@ -502,21 +524,21 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -502,21 +524,21 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredMaxPasswordLifetimeDuration(4*24) # password expire in 4 days preference.setPreferredMaxPasswordLifetimeDuration(4*24) # password expire in 4 days
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired']) self.assertFalse(request['is_user_account_password_expired'])
# test early warning password expire notification is detected # test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24) # password expire notification appear immediately preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24) # password expire notification appear immediately
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertTrue(request['is_user_account_password_expired_expire_date']) self.assertTrue(request['is_user_account_password_expired_expire_date'])
# test early warning password expire notification is detected # test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24-24) # password expire notification appear 3 days befor time preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24-24) # password expire notification appear 3 days befor time
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired_expire_date']) self.assertFalse(request['is_user_account_password_expired_expire_date'])
def test_05_HttpRequest(self): def test_05_HttpRequest(self):
...@@ -525,50 +547,52 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -525,50 +547,52 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
""" """
portal = self.getPortal() portal = self.getPortal()
request = self.app.REQUEST request = self.app.REQUEST
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = self.createUser('test-05')
reference = 'test') assignment = person.newContent(portal_type = 'Assignment')
assignment.open()
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
person.setPassword('used_ALREADY_1234') login.setPassword('used_ALREADY_1234')
self.tic() self.tic()
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue('Welcome to ERP5' in response.getBody()) self.assertTrue('Welcome to ERP5' in response.getBody())
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #1 # fail request #1
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'bad_test') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'bad_test')
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #2 # fail request #2
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #3 # fail request #3
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
self.tic() self.tic()
# test message that account is blocked # test message that account is blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked.")) self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked."))
# test expire password message, first unblock it # test expire password message, first unblock it
person.Person_unblockLogin() login.Login_unblockLogin()
preference.setPreferredMaxPasswordLifetimeDuration(0) preference.setPreferredMaxPasswordLifetimeDuration(0)
self.tic() self.tic()
self._clearCache() self._clearCache()
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Password is expired.")) self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Password is expired."))
self.assertTrue(person.isPasswordExpired()) self.assertTrue(login.isPasswordExpired())
# test we're redirected to update password due to soon expire # test we're redirected to update password due to soon expire
preference.setPreferredMaxPasswordLifetimeDuration(24) preference.setPreferredMaxPasswordLifetimeDuration(24)
...@@ -584,7 +608,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -584,7 +608,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredPasswordLifetimeExpireWarningDuration(12) preference.setPreferredPasswordLifetimeExpireWarningDuration(12)
self.tic() self.tic()
self._clearCache() self._clearCache()
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue('Welcome to ERP5' in response.getBody()) self.assertTrue('Welcome to ERP5' in response.getBody())
...@@ -593,18 +617,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -593,18 +617,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
Check that expiring old Authentication Event list works. Check that expiring old Authentication Event list works.
""" """
portal = self.getPortal() portal = self.getPortal()
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = self.createUser('test-06')
reference = 'test') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
# file some failures so we should detect and block account # file some failures so we should detect and block account
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.tic() self.tic()
# should be blocked # should be blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set 0 check interval # set 0 check interval
preference.setPreferredAuthenticationFailureCheckDuration(0) preference.setPreferredAuthenticationFailureCheckDuration(0)
...@@ -612,14 +636,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -612,14 +636,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
time.sleep(1) # we need to give a moment time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# expire manually old # expire manually old
portal.system_event_module.SystemEventModule_expireAuthenticationEventList() portal.system_event_module.SystemEventModule_expireAuthenticationEventList()
self.tic() self.tic()
self.assertEqual(3, len(portal.portal_catalog(portal_type ="Authentication Event", self.assertEqual(3, len(portal.portal_catalog(portal_type ="Authentication Event",
default_destination_uid = person.getUid(), default_destination_uid = login.getUid(),
validation_state = "expired"))) validation_state = "expired")))
......
...@@ -430,6 +430,7 @@ class TestERP5Credential(ERP5TypeTestCase): ...@@ -430,6 +430,7 @@ class TestERP5Credential(ERP5TypeTestCase):
credential_update = credential_update_module.newContent(\ credential_update = credential_update_module.newContent(\
first_name='Homie', first_name='Homie',
last_name='Simpsons', # add a 's' to the end of the last_name last_name='Simpsons', # add a 's' to the end of the last_name
reference='homie',
password='new_password', password='new_password',
default_email_text='homie.simpsons@fox.com', default_email_text='homie.simpsons@fox.com',
destination_decision=homie.getRelativeUrl()) destination_decision=homie.getRelativeUrl())
...@@ -548,13 +549,19 @@ class TestERP5Credential(ERP5TypeTestCase): ...@@ -548,13 +549,19 @@ class TestERP5Credential(ERP5TypeTestCase):
person_module = portal.getDefaultModule('Person') person_module = portal.getDefaultModule('Person')
person = person_module.newContent(title='Barney', person = person_module.newContent(title='Barney',
reference='barney', reference='barney',
password='secret',
start_date=DateTime('1970/01/01'), start_date=DateTime('1970/01/01'),
default_email_text='barney@duff.com') default_email_text='barney@duff.com')
# create an assignment # create an assignment
assignment = person.newContent(portal_type='Assignment', assignment = person.newContent(portal_type='Assignment',
function='member') function='member')
assignment.open() assignment.open()
# create a login
login = person.newContent(
portal_type='ERP5 Login',
reference=person.getReference(),
password='secret',
)
login.validate()
sequence.edit(person_reference=person.getReference(), sequence.edit(person_reference=person.getReference(),
default_email_text=person.getDefaultEmailText()) default_email_text=person.getDefaultEmailText())
...@@ -572,12 +579,18 @@ class TestERP5Credential(ERP5TypeTestCase): ...@@ -572,12 +579,18 @@ class TestERP5Credential(ERP5TypeTestCase):
person_module = portal.getDefaultModule('Person') person_module = portal.getDefaultModule('Person')
person = person_module.newContent(title=reference, person = person_module.newContent(title=reference,
reference=reference, reference=reference,
password='secret',
default_email_text=default_email_text) default_email_text=default_email_text)
# create an assignment # create an assignment
assignment = person.newContent(portal_type='Assignment', assignment = person.newContent(portal_type='Assignment',
function='member') function='member')
assignment.open() assignment.open()
# create a login
login = person.newContent(
portal_type='ERP5 Login',
reference=person.getReference(),
password='secret',
)
login.validate()
person_list.append(person) person_list.append(person)
sequence.edit(person_list=person_list, sequence.edit(person_list=person_list,
......
...@@ -1280,6 +1280,7 @@ Hé Hé Hé!""", page.asText().strip()) ...@@ -1280,6 +1280,7 @@ Hé Hé Hé!""", page.asText().strip())
# authenticated # authenticated
user = self.createUser('webmaster') user = self.createUser('webmaster')
self.createUserAssignement(user, {}) self.createUserAssignement(user, {})
self.tic()
response = self.publish(path, 'webmaster:webmaster') response = self.publish(path, 'webmaster:webmaster')
last_modified_header = response.getHeader('Last-Modified') last_modified_header = response.getHeader('Last-Modified')
self.assertTrue(last_modified_header) self.assertTrue(last_modified_header)
......
...@@ -88,7 +88,6 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin): ...@@ -88,7 +88,6 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
'career_function': self.getFunction(), 'career_function': self.getFunction(),
'last_name': self.getLastName(), 'last_name': self.getLastName(),
'reference': self.getReference(), 'reference': self.getReference(),
'password': self.getPassword(),
}) })
assignment = person.newContent(portal_type="Assignment", assignment = person.newContent(portal_type="Assignment",
...@@ -96,6 +95,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin): ...@@ -96,6 +95,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
group = group_id, group = group_id,
site = site_id) site = site_id)
login = person.newContent(portal_type='ERP5 Login',
reference=self.getReference(),
password=self.getPassword())
# Set dates are required to create valid assigments. # Set dates are required to create valid assigments.
now = DateTime() now = DateTime()
assignment.setStartDate(now) assignment.setStartDate(now)
...@@ -103,9 +106,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin): ...@@ -103,9 +106,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
# Define valid for 10 years. # Define valid for 10 years.
assignment.setStopDate(now + (365*10)) assignment.setStopDate(now + (365*10))
# Validate the Person and Assigment # Validate the Person, Assigment and Login
person.validate(comment=translateString("Validated by Configurator")) person.validate(comment=translateString("Validated by Configurator"))
assignment.open(comment=translateString("Open by Configuration")) assignment.open(comment=translateString("Open by Configuration"))
login.validate(comment=translateString("Validated by Configurator"))
## add to customer template ## add to customer template
business_configuration = self.getBusinessConfigurationValue() business_configuration = self.getBusinessConfigurationValue()
......
...@@ -82,6 +82,7 @@ class ERP5AccessTokenExtractionPlugin(BasePlugin): ...@@ -82,6 +82,7 @@ class ERP5AccessTokenExtractionPlugin(BasePlugin):
if external_login is not None: if external_login is not None:
creds['external_login'] = external_login creds['external_login'] = external_login
creds['login_portal_type'] = 'Person'
creds['remote_host'] = request.get('REMOTE_HOST', '') creds['remote_host'] = request.get('REMOTE_HOST', '')
try: try:
creds['remote_address'] = request.getClientAddr() creds['remote_address'] = request.getClientAddr()
......
...@@ -32,14 +32,13 @@ from AccessControl import ClassSecurityInfo ...@@ -32,14 +32,13 @@ from AccessControl import ClassSecurityInfo
from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PluggableAuthService.interfaces import plugins from Products.PluggableAuthService.interfaces import plugins
from Products.PluggableAuthService.utils import classImplements from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.ERP5Security.ERP5UserManager import SUPER_USER from Products.ERP5Security.ERP5UserManager import SUPER_USER
from Products.PluggableAuthService.PluggableAuthService import DumbHTTPExtractor from Products.PluggableAuthService.PluggableAuthService import DumbHTTPExtractor
from AccessControl.SecurityManagement import getSecurityManager, \ from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, newSecurityManager setSecurityManager, newSecurityManager
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import socket import socket
from Products.ERP5Security.ERP5UserManager import getUserByLogin from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from zLOG import LOG, ERROR, INFO from zLOG import LOG, ERROR, INFO
try: try:
...@@ -90,7 +89,7 @@ def addERP5GoogleExtractionPlugin(dispatcher, id, title=None, REQUEST=None): ...@@ -90,7 +89,7 @@ def addERP5GoogleExtractionPlugin(dispatcher, id, title=None, REQUEST=None):
'ERP5GoogleExtractionPlugin+added.' 'ERP5GoogleExtractionPlugin+added.'
% dispatcher.absolute_url()) % dispatcher.absolute_url())
class ERP5ExternalOauth2ExtractionPlugin: class ERP5ExternalOauth2ExtractionPluginBase(BasePlugin):
cache_factory_name = 'extrenal_oauth2_token_cache_factory' cache_factory_name = 'extrenal_oauth2_token_cache_factory'
security = ClassSecurityInfo() security = ClassSecurityInfo()
...@@ -143,7 +142,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -143,7 +142,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
Base_createOauth2User = getattr(self.getPortalObject(), Base_createOauth2User = getattr(self.getPortalObject(),
'Base_createOauth2User', None) 'Base_createOauth2User', None)
if Base_createOauth2User is None: if Base_createOauth2User is None:
LOG('ERP5ExternalOauth2ExtractionPlugin', INFO, LOG(self.getId(), INFO,
'No Base_createOauth2User script available, install ' 'No Base_createOauth2User script available, install '
'erp5_credential_oauth2, disabled authentication.') 'erp5_credential_oauth2, disabled authentication.')
return DumbHTTPExtractor().extractCredentials(request) return DumbHTTPExtractor().extractCredentials(request)
...@@ -158,8 +157,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -158,8 +157,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
token = l[1] token = l[1]
if token is None: if token is None:
# no token return creds
return DumbHTTPExtractor().extractCredentials(request)
# token is available # token is available
user = None user = None
...@@ -172,8 +170,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -172,8 +170,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
user = user_entry['reference'] user = user_entry['reference']
if user is None: if user is None:
# fallback to default way return creds
return DumbHTTPExtractor().extractCredentials(request)
tag = '%s_user_creation_in_progress' % user.encode('hex') tag = '%s_user_creation_in_progress' % user.encode('hex')
...@@ -181,7 +178,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -181,7 +178,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
self.REQUEST['USER_CREATION_IN_PROGRESS'] = user self.REQUEST['USER_CREATION_IN_PROGRESS'] = user
else: else:
# create the user if not found # create the user if not found
person_list = getUserByLogin(self.getPortalObject(), user) person_list = self.erp5_users.getPersonByReference(user)
if len(person_list) == 0: if len(person_list) == 0:
sm = getSecurityManager() sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER: if sm.getUser().getId() != SUPER_USER:
...@@ -193,7 +190,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -193,7 +190,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
try: try:
self.Base_createOauth2User(tag, **user_entry) self.Base_createOauth2User(tag, **user_entry)
except Exception: except Exception:
LOG('ERP5ExternalOauth2ExtractionPlugin', ERROR, LOG(self.getId(), ERROR,
'Issue while calling creation script:', error=True) 'Issue while calling creation script:', error=True)
raise raise
finally: finally:
...@@ -204,6 +201,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -204,6 +201,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
# allow to work w/o cache # allow to work w/o cache
pass pass
creds['external_login'] = user creds['external_login'] = user
creds['login_portal_type'] = self.login_portal_type
creds['remote_host'] = request.get('REMOTE_HOST', '') creds['remote_host'] = request.get('REMOTE_HOST', '')
try: try:
creds['remote_address'] = request.getClientAddr() creds['remote_address'] = request.getClientAddr()
...@@ -211,7 +209,7 @@ class ERP5ExternalOauth2ExtractionPlugin: ...@@ -211,7 +209,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
creds['remote_address'] = request.get('REMOTE_ADDR', '') creds['remote_address'] = request.get('REMOTE_ADDR', '')
return creds return creds
class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin): class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPluginBase):
""" """
Plugin to authenicate as machines. Plugin to authenicate as machines.
""" """
...@@ -219,6 +217,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi ...@@ -219,6 +217,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi
meta_type = "ERP5 Facebook Extraction Plugin" meta_type = "ERP5 Facebook Extraction Plugin"
prefix = 'fb_' prefix = 'fb_'
header_string = 'facebook' header_string = 'facebook'
login_portal_type = 'Facebook Login'
def getUserEntry(self, token): def getUserEntry(self, token):
if facebook is None: if facebook is None:
...@@ -250,7 +249,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi ...@@ -250,7 +249,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi
user_entry = None user_entry = None
return user_entry return user_entry
class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin): class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPluginBase):
""" """
Plugin to authenicate as machines. Plugin to authenicate as machines.
""" """
...@@ -258,6 +257,7 @@ class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin) ...@@ -258,6 +257,7 @@ class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin)
meta_type = "ERP5 Google Extraction Plugin" meta_type = "ERP5 Google Extraction Plugin"
prefix = 'go_' prefix = 'go_'
header_string = 'google' header_string = 'google'
login_portal_type = 'Google Login'
def getUserEntry(self, token): def getUserEntry(self, token):
if httplib2 is None: if httplib2 is None:
......
...@@ -25,14 +25,13 @@ from Products.ERP5Type.Cache import CachingMethod ...@@ -25,14 +25,13 @@ from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.ERP5Type \ from Products.ERP5Type.ERP5Type \
import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import sys import sys
from zLOG import LOG, WARNING from zLOG import LOG, WARNING
from ERP5UserManager import SUPER_USER from ERP5UserManager import SUPER_USER, getUserByLogin
# It can be useful to set NO_CACHE_MODE to 1 in order to debug # It can be useful to set NO_CACHE_MODE to 1 in order to debug
# complex security issues related to caching groups. For example, # complex security issues related to caching groups. For example,
...@@ -45,8 +44,8 @@ NO_CACHE_MODE = 0 ...@@ -45,8 +44,8 @@ NO_CACHE_MODE = 0
class ConsistencyError(Exception): pass class ConsistencyError(Exception): pass
manage_addERP5GroupManagerForm = PageTemplateFile( manage_addERP5GroupManagerForm = PageTemplateFile(
'www/ERP5Security_addERP5GroupManager', globals(), 'www/ERP5Security_addERP5GroupManager', globals(),
__name__='manage_addERP5GroupManagerForm' ) __name__='manage_addERP5GroupManagerForm' )
def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ): def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ):
""" Add a ERP5GroupManager to a Pluggable Auth Service. """ """ Add a ERP5GroupManager to a Pluggable Auth Service. """
...@@ -56,10 +55,10 @@ def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ): ...@@ -56,10 +55,10 @@ def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ):
if REQUEST is not None: if REQUEST is not None:
REQUEST['RESPONSE'].redirect( REQUEST['RESPONSE'].redirect(
'%s/manage_workspace' '%s/manage_workspace'
'?manage_tabs_message=' '?manage_tabs_message='
'ERP5GroupManager+added.' 'ERP5GroupManager+added.'
% dispatcher.absolute_url()) % dispatcher.absolute_url())
class ERP5GroupManager(BasePlugin): class ERP5GroupManager(BasePlugin):
...@@ -117,17 +116,10 @@ class ERP5GroupManager(BasePlugin): ...@@ -117,17 +116,10 @@ class ERP5GroupManager(BasePlugin):
else: else:
security_definition_list = mapping_method() security_definition_list = mapping_method()
# get the person from its reference - no security check needed # get the person from its login - no security check needed
catalog_result = self.portal_catalog.unrestrictedSearchResults( person_object = self.erp5_users.getPersonByReference(user_name)
portal_type="Person", query=SimpleQuery(reference=user_name)) if person_object is None: # no person is linked to this user login
if len(catalog_result) != 1: # we won't proceed with groups return ()
if len(catalog_result) > 1: # configuration is screwed
raise ConsistencyError, 'There is more than one Person whose \
login is %s : %s' % (user_name,
repr([r.getObject() for r in catalog_result]))
else: # no person is linked to this user login
return ()
person_object = catalog_result[0].getObject()
# Fetch category values from defined scripts # Fetch category values from defined scripts
for (method_name, base_category_list) in security_definition_list: for (method_name, base_category_list) in security_definition_list:
......
...@@ -46,8 +46,7 @@ from Products.PluggableAuthService.plugins.CookieAuthHelper import CookieAuthHel ...@@ -46,8 +46,7 @@ from Products.PluggableAuthService.plugins.CookieAuthHelper import CookieAuthHel
from Products.ERP5Type.Cache import CachingMethod from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Security.ERP5UserManager import ERP5UserManager, \ from Products.ERP5Security.ERP5UserManager import SUPER_USER,\
SUPER_USER, \
_AuthenticationFailure _AuthenticationFailure
from Crypto.Cipher import AES from Crypto.Cipher import AES
...@@ -130,8 +129,8 @@ class ILoginEncryptionPlugin(Interface): ...@@ -130,8 +129,8 @@ class ILoginEncryptionPlugin(Interface):
#Form for new plugin in ZMI #Form for new plugin in ZMI
manage_addERP5KeyAuthPluginForm = PageTemplateFile( manage_addERP5KeyAuthPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5KeyAuthPlugin', globals(), 'www/ERP5Security_addERP5KeyAuthPlugin', globals(),
__name__='manage_addERP5KeyAuthPluginForm') __name__='manage_addERP5KeyAuthPluginForm')
def addERP5KeyAuthPlugin(dispatcher, id, title=None, def addERP5KeyAuthPlugin(dispatcher, id, title=None,
encryption_key='', cipher='AES', cookie_name='', encryption_key='', cipher='AES', cookie_name='',
...@@ -150,7 +149,7 @@ def addERP5KeyAuthPlugin(dispatcher, id, title=None, ...@@ -150,7 +149,7 @@ def addERP5KeyAuthPlugin(dispatcher, id, title=None,
'ERP5KeyAuthPlugin+added.' 'ERP5KeyAuthPlugin+added.'
% dispatcher.absolute_url()) % dispatcher.absolute_url())
class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): class ERP5KeyAuthPlugin(CookieAuthHelper):
""" """
Key authentification PAS plugin which support key authentication in URL. Key authentification PAS plugin which support key authentication in URL.
...@@ -237,38 +236,17 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -237,38 +236,17 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
#Search __ac_key #Search __ac_key
key = request.get('__ac_key', None) key = request.get('__ac_key', None)
if key is not None: if key is not None:
creds['key'] = key creds['external_login'] = self.decrypt(key)
#Save this in cookie #Save this in cookie
self.updateCredentials(request, request["RESPONSE"], None, None) self.updateCredentials(request, request["RESPONSE"], None, None)
else: else:
# Look in the request for the names coming from the login form #search in cookies
#It's default method cookie = request.get(self.cookie_name, None)
login_pw = request._authUserPW() if cookie is not None:
#Cookie is found
if login_pw is not None: cookie_val = unquote(cookie)
name, password = login_pw creds['external_login'] = self.decrypt(cookie_val)
creds[ 'login' ] = name
creds[ 'password' ] = password
#Save this in cookie
self.updateCredentials(request, request["RESPONSE"], name, password)
else:
#search in cookies
cookie = request.get(self.cookie_name, None)
if cookie is not None:
#Cookie is found
cookie_val = unquote(cookie)
creds['key'] = cookie_val
else:
#Default cookie if needed
default_cookie = request.get(self.default_cookie_name, None)
if default_cookie is not None:
#Cookie is found
cookie_val = decodestring(unquote(default_cookie))
if cookie_val is not None:
login, password = cookie_val.split(':')
creds['login'] = login
creds['password'] = password
#Complete credential with some information #Complete credential with some information
if creds: if creds:
...@@ -315,78 +293,15 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -315,78 +293,15 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
response.expireCookie(self.default_cookie_name, path='/') response.expireCookie(self.default_cookie_name, path='/')
################################
# IAuthenticationPlugin #
################################
security.declarePrivate('authenticateCredentials')
def authenticateCredentials( self, credentials ):
"""Authentificate with credentials"""
key = credentials.get('key', None)
if key != None:
login = self.decrypt(key)
# Forbidden the usage of the super user.
if login == SUPER_USER:
return None
#Function to allow cache
@UnrestrictedMethod
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]
if True:
try:
# get assignment list
assignment_list = [x for x in user.contentValues(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.hasStopDate() and \
assignment.getStopDate() < login_date:
continue
valid_assignment_list.append(assignment)
# validate
if len(valid_assignment_list) > 0:
return (login, login)
finally:
pass
raise _AuthenticationFailure()
#Cache Method for best performance
_authenticateCredentials = CachingMethod(_authenticateCredentials,
id='ERP5KeyAuthPlugin_authenticateCredentials',
cache_factory='erp5_content_short')
try:
return _authenticateCredentials(login=login)
except _AuthenticationFailure:
return None
except StandardError, e:
#Log standard error
LOG('ERP5KeyAuthPlugin.authenticateCredentials', PROBLEM, str(e))
return None
################################ ################################
# Properties for ZMI managment # # Properties for ZMI managment #
################################ ################################
#'Edit' option form #'Edit' option form
manage_editERP5KeyAuthPluginForm = PageTemplateFile( manage_editERP5KeyAuthPluginForm = PageTemplateFile(
'www/ERP5Security_editERP5KeyAuthPlugin', 'www/ERP5Security_editERP5KeyAuthPlugin',
globals(), globals(),
__name__='manage_editERP5KeyAuthPluginForm' ) __name__='manage_editERP5KeyAuthPluginForm' )
security.declareProtected( ManageUsers, 'manage_editKeyAuthPlugin' ) security.declareProtected( ManageUsers, 'manage_editKeyAuthPlugin' )
def manage_editKeyAuthPlugin(self, encryption_key, cipher, cookie_name, def manage_editKeyAuthPlugin(self, encryption_key, cipher, cookie_name,
......
...@@ -70,8 +70,8 @@ def getUserByLogin(portal, login, exact_match=True): ...@@ -70,8 +70,8 @@ def getUserByLogin(portal, login, exact_match=True):
if not (portal.portal_catalog.hasColumn('portal_type') and portal.portal_catalog.hasColumn('reference')): if not (portal.portal_catalog.hasColumn('portal_type') and portal.portal_catalog.hasColumn('reference')):
raise RuntimeError('Catalog does not have column information. Make sure RDB is working and disk is not full.') raise RuntimeError('Catalog does not have column information. Make sure RDB is working and disk is not full.')
result = portal.portal_catalog.unrestrictedSearchResults( result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference', select_expression='reference, portal_type',
portal_type="Person", portal_type=("ERP5 Login"),
reference=dict(query=login, key=reference_key)) reference=dict(query=login, key=reference_key))
# XXX: Here, we filter catalog result list ALTHOUGH we did pass # XXX: Here, we filter catalog result list ALTHOUGH we did pass
# parameters to unrestrictedSearchResults to restrict result set. # parameters to unrestrictedSearchResults to restrict result set.
...@@ -88,8 +88,17 @@ def getUserByLogin(portal, login, exact_match=True): ...@@ -88,8 +88,17 @@ def getUserByLogin(portal, login, exact_match=True):
# 1 row in set (0.01 sec) # 1 row in set (0.01 sec)
# "bar OR foo" because of ZSQLCatalog tokenizing searched strings # "bar OR foo" because of ZSQLCatalog tokenizing searched strings
# by default (feature). # by default (feature).
return [x.getObject() for x in result if not exact_match result_list = [x.getObject().getParentValue()
or x['reference'] in login] for x in result if not exact_match
or x['reference'] in login]
if result_list:
return result_list
result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference, portal_type',
portal_type=("Person"),
reference=dict(query=login, key=reference_key))
return [x.getObject() for x in result if not exact_match
or x['reference'] in login]
@transactional_cached(lambda portal, *args: args) @transactional_cached(lambda portal, *args: args)
def getValidAssignmentList(user): def getValidAssignmentList(user):
...@@ -113,6 +122,7 @@ class ERP5UserManager(BasePlugin): ...@@ -113,6 +122,7 @@ class ERP5UserManager(BasePlugin):
""" """
meta_type = 'ERP5 User Manager' meta_type = 'ERP5 User Manager'
login_portal_type = 'ERP5 Login'
security = ClassSecurityInfo() security = ClassSecurityInfo()
...@@ -120,6 +130,52 @@ class ERP5UserManager(BasePlugin): ...@@ -120,6 +130,52 @@ class ERP5UserManager(BasePlugin):
self._id = self.id = id self._id = self.id = id
self.title = title self.title = title
def getLoginPortalType(self):
return self.login_portal_type
def getPersonByReference(self, reference):
def _getPersonRelativeUrlFromReference(reference):
person_url = self.REQUEST.get('_person_cache', {}).get(reference)
portal = self.getPortalObject()
if person_url is not None:
return person_url
else:
person_list = portal.portal_catalog.unrestrictedSearchResults(
select_list=('relative_url', 'reference'),
portal_type='Person',
reference={'query': reference, 'key': 'ExactMatch'},
limit=2
)
l = len(person_list)
if l > 1:
raise RuntimeError, 'More than one Person have login %r' % \
(reference,)
elif l == 1:
self.REQUEST.set('_person_cache', {})
self.REQUEST['_person_cache'][person_list[0]['reference']] = \
person_list[0]['relative_url']
return person_list[0]['relative_url']
person_relative_url = _getPersonRelativeUrlFromReference(reference)
if person_relative_url is not None:
return self.getPortalObject().unrestrictedTraverse(
person_relative_url)
def checkPersonValidity(self, person):
if person.getValidationState() in ('deleted',):
return False
now = DateTime()
for assignment in person.contentValues(portal_type="Assignment"):
if assignment.getValidationState() != "open":
continue
if assignment.hasStartDate() and \
assignment.getStartDate() > now:
continue
if assignment.hasStopDate() and \
assignment.getStopDate() < now:
continue
return True
return False
# #
# IAuthenticationPlugin implementation # IAuthenticationPlugin implementation
# #
...@@ -143,69 +199,84 @@ class ERP5UserManager(BasePlugin): ...@@ -143,69 +199,84 @@ class ERP5UserManager(BasePlugin):
return None return None
@UnrestrictedMethod @UnrestrictedMethod
def _authenticateCredentials(login, password, path, def _authenticateCredentials(login, password, portal_type,
ignore_password=False): ignore_password=False):
if not login or not (password or ignore_password): if not login or not (password or ignore_password):
return None return None, None
user_list = self.getUserByLogin(login) login_object = self.getLoginObject(login, portal_type)
if not user_list: if not login_object:
raise _AuthenticationFailure() raise _AuthenticationFailure(None)
user = user_list[0] if login_object.getPortalType() == 'Person':
# BBB
user = login_object
else:
user = login_object.getParentValue()
try: try:
if self.checkPersonValidity(user) and \
if (ignore_password or pw_validate(user.getPassword(), password)) and \ (ignore_password or self._validatePassword(login_object, password)):
len(getValidAssignmentList(user)) and user \ return user.getReference(), login_object.getRelativeUrl()
.getValidationState() != 'deleted': #user.getCareerRole() == 'internal':
return login, login # use same for user_id and login
finally: finally:
pass pass
raise _AuthenticationFailure() raise _AuthenticationFailure(login_object.getRelativeUrl())
_authenticateCredentials = CachingMethod( _authenticateCredentials = CachingMethod(
_authenticateCredentials, _authenticateCredentials,
id='ERP5UserManager_authenticateCredentials', id=self.__class__.__name__ + '_authenticateCredentials',
cache_factory='erp5_content_short') cache_factory='erp5_content_short')
try: try:
authentication_result = _authenticateCredentials( user_reference, login_url = _authenticateCredentials(
login=login, login=login,
password=credentials.get('password'), password=credentials.get('password'),
path=self.getPhysicalPath(), portal_type=credentials.get('login_portal_type',
self.login_portal_type),
ignore_password=ignore_password) ignore_password=ignore_password)
except _AuthenticationFailure, exception:
user_reference = None
login_url = exception.message or None
except _AuthenticationFailure: if user_reference and '_login_cache' not in self.REQUEST:
authentication_result = None self.REQUEST.set('_login_cache', {})
self.REQUEST['_login_cache'][user_reference] = login_url
if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled(): if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled():
# stop here, no authentication policy enabled # stop here, no authentication policy enabled
# so just return authentication check result # so just return authentication check result
return authentication_result if user_reference:
return (user_reference, user_reference)
else:
return None
# authentication policy enabled, we need person object anyway if login_url is None:
user_list = self.getUserByLogin(credentials.get('login'))
if not user_list:
# not an ERP5 Person object
return None return None
user = user_list[0]
if authentication_result is None: # authentication policy enabled, we need person object anyway
login = self.getPortalObject().unrestrictedTraverse(login_url)
if login and '_person_cache' not in self.REQUEST:
self.REQUEST.set('_person_cache', {})
self.REQUEST['_person_cache'][user_reference] = login.getParentValue().getRelativeUrl()
if user_reference is None:
# file a failed authentication attempt # file a failed authentication attempt
user.notifyLoginFailure() login.notifyLoginFailure()
return None return None
# check if password is expired # check if password is expired
if user.isPasswordExpired(): if login.isPasswordExpired():
user.notifyPasswordExpire() login.notifyPasswordExpire()
return None return None
# check if user account is blocked # check if login is blocked
if user.isLoginBlocked(): if login.isLoginBlocked():
return None return None
return authentication_result return (user_reference, user_reference)
def _validatePassword(self, login_object, password):
return pw_validate(login_object.getPassword(), password)
# #
# IUserEnumerationPlugin implementation # IUserEnumerationPlugin implementation
...@@ -215,7 +286,7 @@ class ERP5UserManager(BasePlugin): ...@@ -215,7 +286,7 @@ class ERP5UserManager(BasePlugin):
sort_by=None, max_results=None, **kw): sort_by=None, max_results=None, **kw):
""" See IUserEnumerationPlugin. """ See IUserEnumerationPlugin.
""" """
if id is None: if not id:
id = login id = login
if isinstance(id, str): if isinstance(id, str):
id = (id,) id = (id,)
...@@ -228,25 +299,70 @@ class ERP5UserManager(BasePlugin): ...@@ -228,25 +299,70 @@ class ERP5UserManager(BasePlugin):
id_list = [] id_list = []
for user_id in id: for user_id in id:
if SUPER_USER == user_id: if SUPER_USER == user_id:
info = { 'id' : SUPER_USER info = {'id' : SUPER_USER,
, 'login' : SUPER_USER 'login' : SUPER_USER,
, 'pluginid' : plugin_id 'pluginid' : plugin_id,
} }
user_info.append(info) user_info.append(info)
else: else:
id_list.append(user_id) id_list.append(user_id)
if id_list: if id_list:
for user in self.getUserByLogin(tuple(id_list), exact_match=exact_match): if exact_match:
info = { 'id' : user.getReference() for reference in id_list:
, 'login' : user.getReference() user = self.getPersonByReference(reference)
, 'pluginid' : plugin_id if user is not None:
} info = {'id': reference,
'login' : reference,
user_info.append(info) 'pluginid': plugin_id,
}
user_info.append(info)
else:
for user in self.getPortalObject().portal_catalog.unrestrictedSearchResults(
select_list=('reference',),
portal_type='Person',
reference={'query': id_list, 'key': 'Keyword'},
):
info = {'id': user['reference'],
'login' : user['reference'],
'pluginid' : plugin_id,
}
user_info.append(info)
return tuple(user_info) return tuple(user_info)
@transactional_cached(lambda self, *args: args)
def getLoginObject(self, login, portal_type):
try:
if not login:
return
catalog_result = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
select_expression=('portal_type', 'reference', 'validation_state'),
portal_type=(portal_type, 'Person'),
reference=dict(query=login, key='ExactMatch'),
sort_on=(('portal_type',),),
)
for x in catalog_result:
if x['portal_type'] != 'Person' and x['validation_state'] != 'validated':
continue
if x['reference'] != login:
continue
x = x.getObject()
if x.objectIds(spec='ERP5 Login'):
continue # Already migrated.
return x
except ConflictError:
raise
except:
LOG('ERP5Security', PROBLEM, 'getLoginObject 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]
def getUserByLogin(self, login, exact_match=True): def getUserByLogin(self, login, exact_match=True):
# Search the Catalog for login and return a list of person objects # Search the Catalog for login and return a list of person objects
# login can be a string or a list of strings # login can be a string or a list of strings
......
...@@ -49,7 +49,7 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -49,7 +49,7 @@ class TestUserManagement(ERP5TypeTestCase):
def getBusinessTemplateList(self): def getBusinessTemplateList(self):
"""List of BT to install. """ """List of BT to install. """
return ('erp5_base',) return ('erp5_base', 'erp5_administration',)
def beforeTearDown(self): def beforeTearDown(self):
"""Clears person module and invalidate caches when tests are finished.""" """Clears person module and invalidate caches when tests are finished."""
...@@ -94,10 +94,11 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -94,10 +94,11 @@ class TestUserManagement(ERP5TypeTestCase):
newSecurityManager(None, user) newSecurityManager(None, user)
def _makePerson(self, open_assignment=1, assignment_start_date=None, def _makePerson(self, open_assignment=1, assignment_start_date=None,
assignment_stop_date=None, **kw): assignment_stop_date=None, tic=True, **kw):
"""Creates a person in person module, and returns the object, after """Creates a person in person module, and returns the object, after
indexing is done. """ indexing is done. """
person_module = self.getPersonModule() person_module = self.getPersonModule()
password = kw.pop('password', None)
new_person = person_module.newContent( new_person = person_module.newContent(
portal_type='Person', **kw) portal_type='Person', **kw)
assignment = new_person.newContent(portal_type = 'Assignment', assignment = new_person.newContent(portal_type = 'Assignment',
...@@ -105,7 +106,14 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -105,7 +106,14 @@ class TestUserManagement(ERP5TypeTestCase):
stop_date=assignment_stop_date,) stop_date=assignment_stop_date,)
if open_assignment: if open_assignment:
assignment.open() assignment.open()
self.tic() if new_person.hasReference():
login = new_person.newContent(
portal_type='ERP5 Login',
reference=new_person.getReference(),
password=password,)
login.validate()
if tic:
self.tic()
return new_person return new_person
def _assertUserExists(self, login, password): def _assertUserExists(self, login, password):
...@@ -303,9 +311,21 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -303,9 +311,21 @@ class TestUserManagement(ERP5TypeTestCase):
self.tic() self.tic()
self._assertUserExists('the_user', 'secret') self._assertUserExists('the_user', 'secret')
self.loginAsUser('the_user') self.loginAsUser('the_user')
self.portal.REQUEST.set('current_password', 'secret') login = [x for x in pers.objectValues(portal_type='ERP5 Login')][0]
self.portal.REQUEST.set('new_password', 'new_secret') result = self.portal.portal_preferences.PreferenceTool_setNewPassword(
self.portal.portal_preferences.PreferenceTool_setNewPassword() dialog_id='PreferenceTool_viewChangePasswordDialog',
reference=login.getReference(),
current_password='wrong_secret',
new_password='new_secret',
)
self.assertEqual(result, self.portal.absolute_url()+'/portal_preferences/PreferenceTool_viewChangePasswordDialog?portal_status_message=Current%20password%20is%20wrong.')
result = self.portal.portal_preferences.PreferenceTool_setNewPassword(
dialog_id='PreferenceTool_viewChangePasswordDialog',
reference=login.getReference(),
current_password='secret',
new_password='new_secret',
)
self.assertEqual(result, self.portal.absolute_url()+'/logout')
self._assertUserExists('the_user', 'new_secret') self._assertUserExists('the_user', 'new_secret')
self._assertUserDoesNotExists('the_user', 'secret') self._assertUserDoesNotExists('the_user', 'secret')
...@@ -327,8 +347,14 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -327,8 +347,14 @@ class TestUserManagement(ERP5TypeTestCase):
self._assertUserDoesNotExists('the_user', 'secret') self._assertUserDoesNotExists('the_user', 'secret')
def test_PersonNotIndexedNotCached(self): def test_PersonNotIndexedNotCached(self):
pers = self._makePerson(password='secret',) pers = self._makePerson()
pers.setReference('the_user') pers.setReference('the_user')
login = pers.newContent(
portal_type='ERP5 Login',
reference='the_user',
password='secret',
)
login.validate()
# not indexed yet # not indexed yet
self._assertUserDoesNotExists('the_user', 'secret') self._assertUserDoesNotExists('the_user', 'secret')
...@@ -339,9 +365,27 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -339,9 +365,27 @@ class TestUserManagement(ERP5TypeTestCase):
def test_PersonNotValidNotCached(self): def test_PersonNotValidNotCached(self):
pers = self._makePerson(reference='the_user', password='other',) pers = self._makePerson(reference='the_user', password='other',)
self._assertUserDoesNotExists('the_user', 'secret') self._assertUserDoesNotExists('the_user', 'secret')
pers.setPassword('secret') login = pers.objectValues(portal_type='ERP5 Login')[0]
login.setPassword('secret')
self._assertUserExists('the_user', 'secret') self._assertUserExists('the_user', 'secret')
def test_PersonLoginMigration(self):
pers = self._makePerson()
pers.setReference('the_user')
pers.setPassword('secret')
self.assertEqual(len(pers.objectValues(portal_type='ERP5 Login')), 0)
self.tic()
self._assertUserExists('the_user', 'secret')
pers.fixConsistency(filter={'constraint_type': 'post_upgrade'})
self.portal.portal_caches.clearAllCache()
self.tic()
self._assertUserExists('the_user', 'secret')
login = pers.objectValues(portal_type='ERP5 Login')[0]
login.setPassword('secret2')
self.portal.portal_caches.clearAllCache()
self.tic()
self._assertUserDoesNotExists('the_user', 'secret')
self._assertUserExists('the_user', 'secret2')
def test_AssignmentWithDate(self): def test_AssignmentWithDate(self):
"""Tests a person with an assignment with correct date is a valid user.""" """Tests a person with an assignment with correct date is a valid user."""
...@@ -402,6 +446,32 @@ class TestUserManagement(ERP5TypeTestCase): ...@@ -402,6 +446,32 @@ class TestUserManagement(ERP5TypeTestCase):
self.tic() self.tic()
self.assertEqual(None, person.getReference()) self.assertEqual(None, person.getReference())
def test_duplicatePersonReference(self):
person1 = self._makePerson(reference='foo', password='secret',)
self.tic()
self.assertRaises(RuntimeError, self._makePerson,
reference='foo', password='secret',)
def test_duplicateLoginReference(self):
person1 = self._makePerson(reference='foo', password='secret',)
self.tic()
person2 = self._makePerson(reference='bar', password='secret',)
login = person2.objectValues(portal_type='ERP5 Login')[0]
self.assertRaises(RuntimeError, login.setReference, 'foo')
def test_duplicateLoginReferenceInSameTransaction(self):
person1 = self._makePerson(reference='foo', password='secret', tic=False)
person2 = self._makePerson(reference='bar', password='secret', tic=False)
login = person2.newContent(portal_type='ERP5 Login')
self.assertRaises(RuntimeError, login.setReference, 'foo')
def test_duplicateLoginReferenceInAnotherTransaction(self):
person1 = self._makePerson(reference='foo', password='secret', tic=False)
person2 = self._makePerson(reference='bar', password='secret', tic=False)
self.commit()
login = person2.newContent(portal_type='ERP5 Login')
self.assertRaises(RuntimeError, login.setReference, 'foo')
class TestUserManagementExternalAuthentication(TestUserManagement): class TestUserManagementExternalAuthentication(TestUserManagement):
def getTitle(self): def getTitle(self):
"""Title of the test.""" """Title of the test."""
...@@ -484,12 +554,9 @@ class TestLocalRoleManagement(ERP5TypeTestCase): ...@@ -484,12 +554,9 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
base_cat.newContent(portal_type='Category', base_cat.newContent(portal_type='Category',
id='subcat', id='subcat',
codification="%s1" % code) codification="%s1" % code)
# add another function subcategory. base_cat.newContent(portal_type='Category',
function_category = category_tool['function'] id='another_subcat',
if function_category.get('another_subcat', None) is None: codification="%s2" % code)
function_category.newContent(portal_type='Category',
id='another_subcat',
codification='F2')
self.defined_category = "group/subcat\n"\ self.defined_category = "group/subcat\n"\
"site/subcat\n"\ "site/subcat\n"\
"function/subcat" "function/subcat"
...@@ -543,7 +610,7 @@ class TestLocalRoleManagement(ERP5TypeTestCase): ...@@ -543,7 +610,7 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
def getBusinessTemplateList(self): def getBusinessTemplateList(self):
"""List of BT to install. """ """List of BT to install. """
return ('erp5_base', 'erp5_web', 'erp5_ingestion', 'erp5_dms',) return ('erp5_base', 'erp5_web', 'erp5_ingestion', 'erp5_dms', 'erp5_administration')
def test_RolesManagerInterfaces(self): def test_RolesManagerInterfaces(self):
"""Tests group manager plugin respects interfaces.""" """Tests group manager plugin respects interfaces."""
...@@ -575,6 +642,30 @@ class TestLocalRoleManagement(ERP5TypeTestCase): ...@@ -575,6 +642,30 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
self.assertEqual(['Assignor'], obj.__ac_local_roles__.get('F1_G1_S1')) self.assertEqual(['Assignor'], obj.__ac_local_roles__.get('F1_G1_S1'))
self.assertTrue('Assignor' in user.getRolesInContext(obj)) self.assertTrue('Assignor' in user.getRolesInContext(obj))
self.assertFalse('Assignee' in user.getRolesInContext(obj)) self.assertFalse('Assignee' in user.getRolesInContext(obj))
# check if assignment change is effective immediately
self.login()
res = self.publish(self.portal.absolute_url_path() + \
'/Base_viewSecurity?__ac_name=%s&__ac_password=%s' % \
(self.username, self.username))
self.assertEqual([x for x in res.body.splitlines() if x.startswith('-->')],
["--> ['F1_G1_S1']"], res.body)
assignment = self.person.newContent( portal_type='Assignment',
group='subcat',
site='subcat',
function='another_subcat' )
assignment.open()
res = self.publish(self.portal.absolute_url_path() + \
'/Base_viewSecurity?__ac_name=%s&__ac_password=%s' % \
(self.username, self.username))
self.assertEqual([x for x in res.body.splitlines() if x.startswith('-->')],
["--> ['F1_G1_S1']", "--> ['F2_G1_S1']"], res.body)
assignment.setGroup('another_subcat')
res = self.publish(self.portal.absolute_url_path() + \
'/Base_viewSecurity?__ac_name=%s&__ac_password=%s' % \
(self.username, self.username))
self.assertEqual([x for x in res.body.splitlines() if x.startswith('-->')],
["--> ['F1_G1_S1']", "--> ['F2_G2_S1']"], res.body)
self.abort() self.abort()
def testLocalRolesGroupId(self): def testLocalRolesGroupId(self):
......
...@@ -305,7 +305,7 @@ class ERP5TypeInformation(XMLObject, ...@@ -305,7 +305,7 @@ class ERP5TypeInformation(XMLObject,
# Document related to a person's assignment or career step # Document related to a person's assignment or career step
'personal_item', 'personal_item',
# Base # Base
'entity', 'entity', 'login',
# LEGACY - needs a warning - XXX-JPS # LEGACY - needs a warning - XXX-JPS
'tax_movement', 'tax_movement',
) )
......
...@@ -429,8 +429,11 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase): ...@@ -429,8 +429,11 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
person = self.portal.person_module.newContent(portal_type='Person', person = self.portal.person_module.newContent(portal_type='Person',
reference=reference, reference=reference,
password=password,
**person_kw) **person_kw)
login = person.newContent(portal_type='ERP5 Login',
reference=reference,
password=password)
login.validate()
return person return person
def createUserAssignment(self, user, assignment_kw): def createUserAssignment(self, user, assignment_kw):
......
...@@ -55,10 +55,14 @@ class ERP5RemoteUserManager(ERP5UserManager): ...@@ -55,10 +55,14 @@ class ERP5RemoteUserManager(ERP5UserManager):
""" """
meta_type = 'ERP5 Remote User Manager' meta_type = 'ERP5 Remote User Manager'
login_portal_type = 'ERP5 Remote Login'
security = ClassSecurityInfo() security = ClassSecurityInfo()
remote_authentication_cache = None remote_authentication_cache = None
def _doRemoteAuthentication(self, login, password): def checkPersonValidity(self, person):
return True # XXX Really ???
def _validatePassword(self, login_object, password):
# Do remote authentication with local ZODB caching # Do remote authentication with local ZODB caching
# Thanks to this it is possible to login to instance, even # Thanks to this it is possible to login to instance, even
# if master authentication server is down # if master authentication server is down
...@@ -69,6 +73,7 @@ class ERP5RemoteUserManager(ERP5UserManager): ...@@ -69,6 +73,7 @@ class ERP5RemoteUserManager(ERP5UserManager):
# #
# any other error is assumed as fatal and results in disallowing # any other error is assumed as fatal and results in disallowing
# authentication and clearing local cache # authentication and clearing local cache
login = login_object.getReference()
if self.remote_authentication_cache is None: if self.remote_authentication_cache is None:
self.remote_authentication_cache = OOBTree() self.remote_authentication_cache = OOBTree()
portal = self.getPortalObject() portal = self.getPortalObject()
...@@ -122,70 +127,6 @@ class ERP5RemoteUserManager(ERP5UserManager): ...@@ -122,70 +127,6 @@ class ERP5RemoteUserManager(ERP5UserManager):
del self.remote_authentication_cache[login] del self.remote_authentication_cache[login]
return result return result
#
# IAuthenticationPlugin implementation
#
security.declarePrivate( 'authenticateCredentials' )
def authenticateCredentials(self, credentials):
""" See IAuthenticationPlugin.
o We expect the credentials to be those returned by
ILoginPasswordExtractionPlugin.
"""
# Forbidden the usage of the super user.
if credentials.get('login') == SUPER_USER:
return None
def _authenticateCredentials(login, password, path):
if not login or not password:
return None
user_list = self.getUserByLogin(login)
if not user_list:
raise _AuthenticationFailure()
user = user_list[0]
sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER:
newSecurityManager(self, self.getUser(SUPER_USER))
try:
# get assignment
assignment_list = [x for x in user.contentValues(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 to remote ERP5 instance
is_authenticated = self._doRemoteAuthentication(login, password)
if is_authenticated:
return login, login
finally:
setSecurityManager(sm)
raise _AuthenticationFailure()
_authenticateCredentials = CachingMethod(_authenticateCredentials,
id='ERP5RemoteUserManager_authenticateCredentials',
cache_factory='erp5_content_short')
try:
return _authenticateCredentials(
login=credentials.get('login'),
password=credentials.get('password'),
path=self.getPhysicalPath())
except _AuthenticationFailure:
return None
classImplements( ERP5RemoteUserManager classImplements( ERP5RemoteUserManager
, IAuthenticationPlugin , IAuthenticationPlugin
, IUserEnumerationPlugin , IUserEnumerationPlugin
......
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