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>
<chain>
<type>Person</type>
<type>ERP5 Login</type>
<workflow>password_interaction_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_analyzePassword</string> </value>
<value> <string>Login_analyzePassword</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getListboxUrl</string> </value>
<value> <string>Login_getListboxUrl</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_isLoginBlocked</string> </value>
<value> <string>Login_isLoginBlocked</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_isPasswordExpired</string> </value>
<value> <string>Login_isPasswordExpired</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -13,27 +13,13 @@ message_dict = { 0: 'Unknown error',
-4: 'You have already used this 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
result_code_list = person.Person_analyzePassword(password)
result_code_list = login.analyzePassword(password)
if result_code_list!=[]:
translateString = context.Base_translateString
message = ' '.join([translateString(message_dict[x]) for x in result_code_list])
raise ValidationError('external_validator_failed', context, error_text=message)
return 1
user_login = request.get('field_user_login', None)
# 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)
return doValidation(context, password)
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isPasswordValid</string> </value>
<value> <string>Login_isPasswordValid</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -63,7 +63,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_notifyLoginFailure</string> </value>
<value> <string>Login_notifyLoginFailure</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -63,7 +63,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_notifyPasswordExpire</string> </value>
<value> <string>Login_notifyPasswordExpire</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_unblockLogin</string> </value>
<value> <string>Login_unblockLogin</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -174,15 +174,15 @@
<list>
<tuple>
<string>title</string>
<string>Person_getListboxUrl</string>
<string>Login_getListboxUrl</string>
</tuple>
<tuple>
<string>reference</string>
<string>Person_getListboxUrl</string>
<string>Login_getListboxUrl</string>
</tuple>
<tuple>
<string>count</string>
<string>Person_getListboxUrl</string>
<string>Login_getListboxUrl</string>
</tuple>
</list>
</value>
......
......@@ -28,7 +28,7 @@
<key> <string>after_script_name</string> </key>
<value>
<list>
<string>Person_changePassword</string>
<string>afterChangePassword</string>
</list>
</value>
</item>
......@@ -72,10 +72,16 @@
<key> <string>portal_type_filter</string> </key>
<value>
<list>
<string>Person</string>
<string>ERP5 Login</string>
</list>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>script_name</string> </key>
<value>
......
from DateTime import DateTime
portal = context.getPortalObject()
person = state_change['object']
login = state_change['object']
portal = login.getPortalObject()
# check preferences and save only if set
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:
# save password and modification date
current_password = person.getPassword()
current_password = login.getPassword()
if current_password is not None:
password_event = portal.system_event_module.newContent(portal_type = 'Password Event',
source_value = person,
destination_value = person,
password = current_password)
password_event = portal.system_event_module.newContent(portal_type='Password Event',
source_value=login,
destination_value=login,
password=current_password)
password_event.confirm()
# 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
......
......@@ -63,7 +63,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_changePassword</string> </value>
<value> <string>afterChangePassword</string> </value>
</item>
</dictionary>
</pickle>
......
Person | password_interaction_workflow
\ No newline at end of file
ERP5 Login | password_interaction_workflow
\ 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 @@
</pickle>
<pickle>
<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>
<key> <string>_count</string> </key>
<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 @@
<item>Career</item>
<item>Chat Address</item>
<item>Credit Card</item>
<item>ERP5 Login</item>
<item>Email</item>
<item>Embedded File</item>
<item>Fax</item>
......
......@@ -8,6 +8,7 @@
</portal_type>
<portal_type id="Person">
<item>DefaultImage</item>
<item>PersonUpgradeConstraint</item>
</portal_type>
<portal_type id="Preference">
<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 @@
<type>Delivery Type</type>
<workflow>base_type_interaction_workflow, dynamic_class_generation_interaction_workflow</workflow>
</chain>
<chain>
<type>ERP5 Login</type>
<workflow>edit_workflow, login_validation_workflow</workflow>
</chain>
<chain>
<type>Email</type>
<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 @@
<string>my_view_mode_telephone_number</string>
<string>my_view_mode_email_coordinate_text</string>
<string>my_view_mode_email_url_string</string>
<string>my_view_mode_portal_type</string>
</list>
</value>
</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 @@
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>editable</string>
<string>enabled</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_reference</string> </value>
<value> <string>my_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
......@@ -54,15 +53,11 @@
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<key> <string>enabled</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
......@@ -82,10 +77,6 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The username this person will use to log in the system.\n
......@@ -93,8 +84,8 @@
The system will check that there isn\'t another user with the same username.</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<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
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
<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: here.portal_membership.getAuthenticatedMember().getUserName()</string> </value>
<value> <string>python: request.AUTHENTICATED_USER.has_permission(\'Manage users\', here)</string> </value>
</item>
</dictionary>
</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 @@
<value>
<list>
<string>listbox</string>
<string>login_listbox</string>
</list>
</value>
</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 Products.ERP5Type.Message import translateString
request = context.REQUEST
new_password = request.get("new_password")
former_password = request.get("current_password")
portal = context.getPortalObject()
user = getSecurityManager().getUser()
persons = context.acl_users.erp5_users.getUserByLogin(user)
person = persons[0]
if not person.checkPassword(former_password):
msg = translateString("Current password is wrong.")
person = context.acl_users.erp5_users.getPersonByReference(user.getId())
for login in person.objectValues(portal_type='ERP5 Login'):
if login.getReference() == reference and login.getValidationState() == 'validated':
break
else:
msg = translateString("Password changed.")
person.setPassword(new_password)
msg = translateString('You cannot change the password for %r.' % reference)
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)
context.portal_caches.clearCache(('erp5_content_short',))
return context.logout()
return portal.Base_redirect('logout')
......@@ -50,7 +50,7 @@
</item>
<item>
<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>
<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
Delivery Type | translation_view
Delivery Type | update_local_roles
Delivery Type | view
ERP5 Login | view
Email | change_function
Email | view
Embedded File | download
......@@ -87,6 +88,7 @@ Person | person_detailed_report
Person | terminate_career
Person | view
Preference | base_preference
Preference | change_password
Previous Causality Movement Group | view
Property Assignment Movement Group | view
Property Grouping Movement Group | view
......@@ -108,4 +110,4 @@ Variant Movement Group | view
Variation Property Movement Group | view
portal_actions | jump_query
portal_actions | jump_related_object
portal_actions | post_query
\ No newline at end of file
portal_actions | post_query
document.erp5.GeographicalArea
document.erp5.GeographicalPoint
\ No newline at end of file
document.erp5.GeographicalPoint
document.erp5.Login
\ No newline at end of file
extension.erp5.BarcodeUtils
extension.erp5.BaseMigration
extension.erp5.BarcodeUtils
\ No newline at end of file
extension.erp5.PersonLoginMigration
\ No newline at end of file
......@@ -76,6 +76,7 @@ Person | Bank Account
Person | Career
Person | Chat Address
Person | Credit Card
Person | ERP5 Login
Person | Email
Person | Embedded File
Person | Fax
......
......@@ -19,6 +19,7 @@ Delivery Builder
Delivery Causality Assignment Movement Group
Delivery Tool
Delivery Type
ERP5 Login
Email
Embedded File
Embedded Folder
......@@ -62,4 +63,4 @@ Split Movement Group
Telephone
Title Movement Group
Variant Movement Group
Variation Property Movement Group
\ No newline at end of file
Variation Property Movement Group
......@@ -2,6 +2,7 @@ Notification Message | ItemAggregation
Notification Message | Reference
Organisation | DefaultImage
Person | DefaultImage
Person | PersonUpgradeConstraint
Preference | TelephonePreference
Previous Causality Movement Group | PreviousCausalityMovementGroup
Query | TextDocument
\ No newline at end of file
......@@ -17,6 +17,8 @@ Currency | edit_workflow
Currency | validation_workflow
Delivery Type | base_type_interaction_workflow
Delivery Type | dynamic_class_generation_interaction_workflow
ERP5 Login | edit_workflow
ERP5 Login | login_validation_workflow
Email | edit_workflow
Embedded File | document_conversion_interaction_workflow
Embedded File | edit_workflow
......@@ -50,4 +52,4 @@ Role Definition | edit_workflow
Role Definition | local_permission_interaction_workflow
Rounding Model | validation_workflow
Simulation Movement | simulation_movement_causality_interaction_workflow
Telephone | edit_workflow
\ No newline at end of file
Telephone | edit_workflow
LoginConstraint
PersonUpgradeConstraint
PreviousCausalityMovementGroup
\ No newline at end of file
......@@ -10,6 +10,7 @@ document_conversion_interaction_workflow
document_security_interaction_workflow
embedded_workflow
local_permission_interaction_workflow
login_validation_workflow
movement_resource_interaction_workflow
notification_message_workflow
person_interaction_workflow
......
......@@ -225,7 +225,6 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin):
person.getFirstName())
self.assertEqual(user_info["field_your_last_name"],
person.getLastName())
self.assertNotEquals(person.getPassword(), None)
self.assertEqual(user_info["field_your_function"],
person.getFunction())
self.assertEqual(user_info["field_your_default_email_text"],
......@@ -236,6 +235,9 @@ class StandardConfigurationMixin(TestLiveConfiguratorWorkflowMixin):
assignment_list = person.contentValues(portal_type='Assignment')
self.assertEqual(len(assignment_list), 1)
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):
"""Check that the social title category is configured.
......
......@@ -50,7 +50,7 @@
</item>
<item>
<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>
<key> <string>_proxy_roles</string> </key>
......
......@@ -93,6 +93,7 @@
<key> <string>left</string> </key>
<value>
<list>
<string>your_reference</string>
<string>your_gender</string>
<string>your_first_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 @@
<value>
<list>
<tuple>
<string>Person</string>
<string>Person</string>
<string>ERP5 Login</string>
<string>ERP5 Login</string>
</tuple>
</list>
</value>
......
......@@ -102,8 +102,8 @@
<value>
<list>
<tuple>
<string>Person</string>
<string>Person</string>
<string>ERP5 Login</string>
<string>ERP5 Login</string>
</tuple>
</list>
</value>
......
......@@ -1536,6 +1536,14 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
return self._getPortalGroupedTypeList('entity') or\
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,
'getDefaultModuleId')
def getDefaultModuleId(self, portal_type, default=MARKER, only_visible=False):
......
......@@ -272,11 +272,10 @@ class PasswordTool(BaseTool):
# XXX: incorrect grammar
return error("Date has expire.")
del self._password_request_dict[password_key]
persons = self.getPortalObject().acl_users.erp5_users.getUserByLogin(
register_user_login)
person = persons[0]
person._forceSetPassword(password)
person.reindexObject()
login = self.getPortalObject().acl_users.erp5_users.getLoginObject(
register_user_login, 'ERP5 Login')
login._forceSetPassword(password)
login.reindexObject()
return redirect(REQUEST, site_url,
translateString("Password changed."))
......
......@@ -53,18 +53,12 @@ def getSecurityCategoryFromAssignment(self, base_category_list, user_name, objec
category_list = []
person_object_list = self.portal_catalog.unrestrictedSearchResults(
query=SimpleQuery(reference=user_name), portal_type='Person')
if len(person_object_list) != 1:
if len(person_object_list) > 1:
raise ConsistencyError, "Error: There is more than one Person with reference '%s'" % user_name
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()
person_object = self.getPortalObject().acl_users.erp5_users.getPersonByReference(user_name)
if person_object is None:
# 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 []
# We look for every valid assignments of this user
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
Base Category | view
Base Domain | view
Base Type | action_view
Base Type | jump_property_sheets
Base Type | role_view
Base Type | translation_view
Base Type | update_local_roles
......@@ -80,17 +79,14 @@ Id Tool | view
Memcached Plugin | view
Memcached Tool | view
Predicate | view
Preference Tool Type | jump_property_sheets
Preference Tool Type | view
Preference Tool | advanced
Preference Tool | view
Preference Type | action_view
Preference Type | jump_property_sheets
Preference Type | role_view
Preference Type | translation_view
Preference Type | update_local_roles
Preference Type | view
Preference | change_password
Preference | html_style_preferences
Preference | view
Preference | view_template
......
......@@ -66,10 +66,12 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
reference = 'test')
if portal.portal_catalog.getResultValue(**kw) is None:
# add a loggable Person
person = portal.person_module.newContent(password = 'test',
first_name = 'First',
last_name = 'Last',
**kw)
person = self.createUser(
kw['reference'],
password='test',
person_kw={'first_name': 'First',
'last_name': 'Last'},
)
person.validate()
assignment = person.newContent(portal_type = 'Assignment')
assignment.open()
......@@ -91,16 +93,33 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
'erp5_content_short', # for authentication cache
))
def _getPasswordEventList(self, person):
def _getPasswordEventList(self, login):
return [x.getObject() for x in self.portal.portal_catalog(
portal_type = 'Password Event',
default_destination_uid = person.getUid(),
default_destination_uid = login.getUid(),
sort_on = (('creation_date', 'DESC',),))]
def _cleanUpPerson(self, person):
self.portal.system_event_module.manage_delObjects([x.getId() for x in self._getPasswordEventList(person)])
def _cleanUpLogin(self, login):
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):
"""
Test that a recataloging works for Web Site documents
......@@ -110,73 +129,74 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
person = portal.portal_catalog.getResultValue(portal_type = 'Person',
reference = 'test')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',)
# login should be allowed
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# file some failures so we should detect and block account
person.notifyLoginFailure()
person.notifyLoginFailure()
person.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
self.tic()
# should be blocked
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
# set check back interval to actualy disable blocking
preference.setPreferredAuthenticationFailureCheckDuration(0)
self._clearCache()
self.tic()
time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# .. and revert it back
preference.setPreferredAuthenticationFailureCheckDuration(600)
self._clearCache()
self.tic()
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
# increase failures attempts
preference.setPreferredMaxAuthenticationFailure(4)
self._clearCache()
self.tic()
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# .. and revert it back
preference.setPreferredMaxAuthenticationFailure(3)
self._clearCache()
self.tic()
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
# set short block interval so we can test it as well
preference.setPreferredAuthenticationFailureBlockDuration(3)
self._clearCache()
self.tic()
time.sleep(4)
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# test multiple concurrent transactions without waiting for activities to be over
preference.setPreferredAuthenticationFailureCheckDuration(600)
preference.setPreferredAuthenticationFailureBlockDuration(600)
preference.setPreferredMaxAuthenticationFailure(3)
person.Person_unblockLogin()
login.Login_unblockLogin()
self._clearCache()
self.tic()
person.notifyLoginFailure()
person.notifyLoginFailure()
person.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
self.commit()
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
self.tic()
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
# test unblock account
person.Person_unblockLogin()
login.Login_unblockLogin()
self.tic()
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
def test_02_PasswordHistory(self):
......@@ -186,61 +206,61 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
portal = self.getPortal()
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person',
reference = 'test-02')
person = self.createUser('test-02')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',)
self.tic()
# 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)
self.tic()
self._clearCache()
person.setPassword('12345678')
login.setPassword('12345678')
self.tic()
# password change date should be saved as well hashed old password value
old_password = person.getPassword()
self.assertSameSet([old_password], [x.getPassword() for x in self._getPasswordEventList(person)])
old_password = login.getPassword()
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
person.setPassword('123456789')
login.setPassword('123456789')
self.tic()
old_password1 = person.getPassword()
old_password1 = login.getPassword()
# 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)...
person._setPassword('123456789-1')
login._setPassword('123456789-1')
self.tic()
old_password2 = person.getPassword()
old_password2 = login.getPassword()
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)...
person._forceSetPassword('123456789-2')
login._forceSetPassword('123456789-2')
self.tic()
old_password3 = person.getPassword()
old_password3 = login.getPassword()
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)...
person.setEncodedPassword('123456789-3')
login.setEncodedPassword('123456789-3')
self.tic()
old_password4 = person.getPassword()
old_password4 = login.getPassword()
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)...
person.edit(password = '123456789-4')
login.edit(password = '123456789-4')
self.tic()
old_password5 = person.getPassword()
old_password5 = login.getPassword()
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):
......@@ -258,105 +278,107 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person',
reference = 'test-03',
password = 'test',
first_name = 'First',
last_name = 'Last')
person = self.createUser(
'test-03',
password='test',
person_kw={'first_name': 'First',
'last_name': 'Last'},
)
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',)
self.tic()
# 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
self._cleanUpPerson(person)
self._cleanUpLogin(login)
preference.setPreferredMinPasswordLength(8)
preference.setPreferredNumberOfLastPasswordToCheck(0)
self.tic()
self._clearCache()
self.assertEqual([-1], person.analyzePassword(''))
self.assertEqual([-1], person.analyzePassword('1234567'))
self.assertTrue(person.isPasswordValid('12345678'))
self.assertEqual([-1], login.analyzePassword(''))
self.assertEqual([-1], login.analyzePassword('1234567'))
self.assertTrue(login.isPasswordValid('12345678'))
# not changed in last x days
self._cleanUpPerson(person)
self._cleanUpLogin(login)
preference.setPreferredMinPasswordLifetimeDuration(24)
preference.setPreferredNumberOfLastPasswordToCheck(3)
self.tic()
self._clearCache()
self.assertTrue(person.isPasswordValid('12345678'))
person.setPassword('12345678')
self.assertTrue(login.isPasswordValid('12345678'))
login.setPassword('12345678')
self.tic()
# if we try to change now we should fail with any password
self.assertSameSet([-3], person.analyzePassword('87654321'))
self.assertSameSet([-1, -3], person.analyzePassword('short')) # multiple failures
self.assertFalse(person.isPasswordValid('short')) # multiple failures
self.assertRaises(ValueError, person.setPassword, '87654321')
self.assertSameSet([-3], login.analyzePassword('87654321'))
self.assertSameSet([-1, -3], login.analyzePassword('short')) # multiple failures
self.assertFalse(login.isPasswordValid('short')) # multiple failures
self.assertRaises(ValueError, login.setPassword, '87654321')
preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction
self.tic()
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
preference.setPreferredMinPasswordLength(None) # disable for now
self._cleanUpPerson(person)
self._cleanUpLogin(login)
self._clearCache()
self.tic()
person.setPassword('12345678-new')
login.setPassword('12345678-new')
self.tic()
self.assertSameSet([-4], person.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password
self.assertRaises(ValueError, person.setPassword, '12345678-new')
self.assertTrue(person.isPasswordValid('12345678_')) # it's OK with another one not used yet
self.assertSameSet([-4], login.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password
self.assertRaises(ValueError, login.setPassword, '12345678-new')
self.assertTrue(login.isPasswordValid('12345678_')) # it's OK with another one not used yet
for password in ['a','b','c','d', 'e', 'f']:
# this sleep is not so beautiful, but mysql datetime columns has a
# precision of one second only, and we use creation_date to order
# "Password Event" objects. So without this sleep, the test is
# failing randomly.
time.sleep(1)
person.setPassword(password)
login.setPassword(password)
self.tic()
self._clearCache()
self.tic()
self.assertTrue(person.isPasswordValid('12345678-new'))
self.assertTrue(person.isPasswordValid('a'))
self.assertTrue(person.isPasswordValid('b'))
self.assertTrue(person.isPasswordValid('c'))
self.assertTrue(login.isPasswordValid('12345678-new'))
self.assertTrue(login.isPasswordValid('a'))
self.assertTrue(login.isPasswordValid('b'))
self.assertTrue(login.isPasswordValid('c'))
# only last 3 (including current one are invalid)
self.assertSameSet([-4], person.analyzePassword('d'))
self.assertSameSet([-4], person.analyzePassword('e'))
self.assertSameSet([-4], person.analyzePassword('f'))
self.assertSameSet([-4], login.analyzePassword('d'))
self.assertSameSet([-4], login.analyzePassword('e'))
self.assertSameSet([-4], login.analyzePassword('f'))
# if we remove restricted then all password are usable
preference.setPreferredNumberOfLastPasswordToCheck(None)
self._clearCache()
self.tic()
self.assertTrue(person.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e'))
self.assertTrue(person.isPasswordValid('f'))
self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(login.isPasswordValid('e'))
self.assertTrue(login.isPasswordValid('f'))
# if we set only last password to check
preference.setPreferredNumberOfLastPasswordToCheck(1)
self._clearCache()
self.tic()
self.assertTrue(person.isPasswordValid('c'))
self.assertTrue(person.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e'))
self.assertSameSet([-4], person.analyzePassword('f'))
self.assertTrue(login.isPasswordValid('c'))
self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(login.isPasswordValid('e'))
self.assertSameSet([-4], login.analyzePassword('f'))
preference.setPreferredRegularExpressionGroupList(regular_expression_list)
preference.setPreferredMinPasswordLength(7)
preference.setPreferredNumberOfLastPasswordToCheck(None)
self._cleanUpPerson(person)
self._cleanUpLogin(login)
self._clearCache()
self.tic()
......@@ -370,47 +392,47 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache()
self.tic()
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:
self.assertSameSet([-2], person.analyzePassword(password))
self.assertSameSet([-2], login.analyzePassword(password))
# min 3 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(3)
self._clearCache()
self._cleanUpPerson(person)
self._cleanUpLogin(login)
self.tic()
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:
self.assertSameSet([-2], person.analyzePassword(password))
self.assertSameSet([-2], login.analyzePassword(password))
# min 2 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(2)
self._clearCache()
self.tic()
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:
self.assertSameSet([-2], person.analyzePassword(password))
self.assertSameSet([-2], login.analyzePassword(password))
# min 1 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(1)
self._clearCache()
self.tic()
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
preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache()
self.tic()
self.assertSameSet([-5], person.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.getFirstName()))
self.assertSameSet([-5], login.analyzePassword('abAB#12_%s' %person.getLastName()))
preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache()
self.tic()
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getFirstName()))
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getLastName()))
self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getFirstName()))
self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getLastName()))
# check on temp objects just passworrd length( i.e. simulating a new user account creation)
first_name = 'John'
......@@ -425,8 +447,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache()
self.tic()
# 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([], temp_person.Person_analyzePassword('longEnough1'))
self.assertSameSet([-1], temp_person.Login_analyzePassword('onlyNine1'))
self.assertSameSet([], temp_person.Login_analyzePassword('longEnough1'))
# make sure re check works on temp as well ( i.e. min 3 out of all groups)
preference.setPreferredRegularExpressionGroupList(regular_expression_list)
......@@ -435,22 +457,22 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache()
self.tic()
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:
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)
preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache()
self.tic()
self.assertSameSet([-5], temp_person.Person_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' %first_name))
self.assertSameSet([-5], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache()
self.tic()
self.assertSameSet([], temp_person.Person_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' %first_name))
self.assertSameSet([], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
# check Base_isPasswordValid is able to work in Anonymous User fashion
# but with already create Person object (i.e. recover password case)
......@@ -461,20 +483,20 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache()
self.tic()
person.setPassword('used_ALREADY_1234')
login.setPassword('used_ALREADY_1234')
self._clearCache()
self.tic()
# emulate Anonymous User
self.logout()
request.set('field_user_login', person.getReference())
self.assertRaises(ValidationError, portal.Base_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, portal.Base_isPasswordValid, 'abAB#1', request) # too short
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abABCDEFG', request) # too few groups
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'used_ALREADY_1234', request) # already used
self.assertEqual(1, portal.Base_isPasswordValid('abAB#12_', request))
self.assertEqual(1, portal.Base_isPasswordValid('not_used_ALREADY_1234', request))
request.set('field_user_login', login.getReference())
self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getFirstName(), request) # contains name
self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getLastName(), request) # contains name
self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#1', request) # too short
self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abABCDEFG', request) # too few groups
self.assertRaises(ValidationError, login.Login_isPasswordValid, 'used_ALREADY_1234', request) # already used
self.assertEqual(1, login.Login_isPasswordValid('abAB#12_', request))
self.assertEqual(1, login.Login_isPasswordValid('not_used_ALREADY_1234', request))
def test_04_PasswordExpire(self):
"""
......@@ -485,16 +507,16 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person',
reference = 'test-04',
password = 'used_ALREADY_1234')
person = self.createUser('test-04',
password='used_ALREADY_1234')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',)
preference.setPreferredMaxPasswordLifetimeDuration(24)
self.tic()
self._clearCache()
self.assertFalse(person.isPasswordExpired())
self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired'])
......@@ -502,21 +524,21 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredMaxPasswordLifetimeDuration(4*24) # password expire in 4 days
self.tic()
self._clearCache()
self.assertFalse(person.isPasswordExpired())
self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired'])
# test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24) # password expire notification appear immediately
self.tic()
self._clearCache()
self.assertFalse(person.isPasswordExpired())
self.assertFalse(login.isPasswordExpired())
self.assertTrue(request['is_user_account_password_expired_expire_date'])
# test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24-24) # password expire notification appear 3 days befor time
self.tic()
self._clearCache()
self.assertFalse(person.isPasswordExpired())
self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired_expire_date'])
def test_05_HttpRequest(self):
......@@ -525,50 +547,52 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
"""
portal = self.getPortal()
request = self.app.REQUEST
person = portal.portal_catalog.getResultValue(portal_type = 'Person',
reference = 'test')
person = self.createUser('test-05')
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',
title = 'Authentication',)
person.setPassword('used_ALREADY_1234')
login.setPassword('used_ALREADY_1234')
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)
self.assertTrue('Welcome to ERP5' in response.getBody())
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# 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)
self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# fail request #2
response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# fail request #3
response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
self.tic()
# test message that account is blocked
self.assertTrue(person.isLoginBlocked())
path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234')
self.assertTrue(login.isLoginBlocked())
path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked."))
# test expire password message, first unblock it
person.Person_unblockLogin()
login.Login_unblockLogin()
preference.setPreferredMaxPasswordLifetimeDuration(0)
self.tic()
self._clearCache()
response = self.publish(path)
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
preference.setPreferredMaxPasswordLifetimeDuration(24)
......@@ -584,7 +608,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredPasswordLifetimeExpireWarningDuration(12)
self.tic()
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)
self.assertTrue('Welcome to ERP5' in response.getBody())
......@@ -593,18 +617,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
Check that expiring old Authentication Event list works.
"""
portal = self.getPortal()
person = portal.portal_catalog.getResultValue(portal_type = 'Person',
reference = 'test')
person = self.createUser('test-06')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',)
# file some failures so we should detect and block account
person.notifyLoginFailure()
person.notifyLoginFailure()
person.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
login.notifyLoginFailure()
self.tic()
# should be blocked
self.assertTrue(person.isLoginBlocked())
self.assertTrue(login.isLoginBlocked())
# set 0 check interval
preference.setPreferredAuthenticationFailureCheckDuration(0)
......@@ -612,14 +636,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache()
time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked())
self.assertFalse(login.isLoginBlocked())
# expire manually old
portal.system_event_module.SystemEventModule_expireAuthenticationEventList()
self.tic()
self.assertEqual(3, len(portal.portal_catalog(portal_type ="Authentication Event",
default_destination_uid = person.getUid(),
default_destination_uid = login.getUid(),
validation_state = "expired")))
......
......@@ -430,6 +430,7 @@ class TestERP5Credential(ERP5TypeTestCase):
credential_update = credential_update_module.newContent(\
first_name='Homie',
last_name='Simpsons', # add a 's' to the end of the last_name
reference='homie',
password='new_password',
default_email_text='homie.simpsons@fox.com',
destination_decision=homie.getRelativeUrl())
......@@ -548,13 +549,19 @@ class TestERP5Credential(ERP5TypeTestCase):
person_module = portal.getDefaultModule('Person')
person = person_module.newContent(title='Barney',
reference='barney',
password='secret',
start_date=DateTime('1970/01/01'),
default_email_text='barney@duff.com')
# create an assignment
assignment = person.newContent(portal_type='Assignment',
function='member')
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(),
default_email_text=person.getDefaultEmailText())
......@@ -572,12 +579,18 @@ class TestERP5Credential(ERP5TypeTestCase):
person_module = portal.getDefaultModule('Person')
person = person_module.newContent(title=reference,
reference=reference,
password='secret',
default_email_text=default_email_text)
# create an assignment
assignment = person.newContent(portal_type='Assignment',
function='member')
assignment.open()
# create a login
login = person.newContent(
portal_type='ERP5 Login',
reference=person.getReference(),
password='secret',
)
login.validate()
person_list.append(person)
sequence.edit(person_list=person_list,
......
......@@ -1280,6 +1280,7 @@ Hé Hé Hé!""", page.asText().strip())
# authenticated
user = self.createUser('webmaster')
self.createUserAssignement(user, {})
self.tic()
response = self.publish(path, 'webmaster:webmaster')
last_modified_header = response.getHeader('Last-Modified')
self.assertTrue(last_modified_header)
......
......@@ -88,7 +88,6 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
'career_function': self.getFunction(),
'last_name': self.getLastName(),
'reference': self.getReference(),
'password': self.getPassword(),
})
assignment = person.newContent(portal_type="Assignment",
......@@ -96,6 +95,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
group = group_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.
now = DateTime()
assignment.setStartDate(now)
......@@ -103,9 +106,10 @@ class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin):
# Define valid for 10 years.
assignment.setStopDate(now + (365*10))
# Validate the Person and Assigment
# Validate the Person, Assigment and Login
person.validate(comment=translateString("Validated by Configurator"))
assignment.open(comment=translateString("Open by Configuration"))
login.validate(comment=translateString("Validated by Configurator"))
## add to customer template
business_configuration = self.getBusinessConfigurationValue()
......
......@@ -82,6 +82,7 @@ class ERP5AccessTokenExtractionPlugin(BasePlugin):
if external_login is not None:
creds['external_login'] = external_login
creds['login_portal_type'] = 'Person'
creds['remote_host'] = request.get('REMOTE_HOST', '')
try:
creds['remote_address'] = request.getClientAddr()
......
......@@ -32,14 +32,13 @@ from AccessControl import ClassSecurityInfo
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PluggableAuthService.interfaces import plugins
from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.ERP5Security.ERP5UserManager import SUPER_USER
from Products.PluggableAuthService.PluggableAuthService import DumbHTTPExtractor
from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, newSecurityManager
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import socket
from Products.ERP5Security.ERP5UserManager import getUserByLogin
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from zLOG import LOG, ERROR, INFO
try:
......@@ -90,7 +89,7 @@ def addERP5GoogleExtractionPlugin(dispatcher, id, title=None, REQUEST=None):
'ERP5GoogleExtractionPlugin+added.'
% dispatcher.absolute_url())
class ERP5ExternalOauth2ExtractionPlugin:
class ERP5ExternalOauth2ExtractionPluginBase(BasePlugin):
cache_factory_name = 'extrenal_oauth2_token_cache_factory'
security = ClassSecurityInfo()
......@@ -143,7 +142,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
Base_createOauth2User = getattr(self.getPortalObject(),
'Base_createOauth2User', None)
if Base_createOauth2User is None:
LOG('ERP5ExternalOauth2ExtractionPlugin', INFO,
LOG(self.getId(), INFO,
'No Base_createOauth2User script available, install '
'erp5_credential_oauth2, disabled authentication.')
return DumbHTTPExtractor().extractCredentials(request)
......@@ -158,8 +157,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
token = l[1]
if token is None:
# no token
return DumbHTTPExtractor().extractCredentials(request)
return creds
# token is available
user = None
......@@ -172,8 +170,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
user = user_entry['reference']
if user is None:
# fallback to default way
return DumbHTTPExtractor().extractCredentials(request)
return creds
tag = '%s_user_creation_in_progress' % user.encode('hex')
......@@ -181,7 +178,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
self.REQUEST['USER_CREATION_IN_PROGRESS'] = user
else:
# create the user if not found
person_list = getUserByLogin(self.getPortalObject(), user)
person_list = self.erp5_users.getPersonByReference(user)
if len(person_list) == 0:
sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER:
......@@ -193,7 +190,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
try:
self.Base_createOauth2User(tag, **user_entry)
except Exception:
LOG('ERP5ExternalOauth2ExtractionPlugin', ERROR,
LOG(self.getId(), ERROR,
'Issue while calling creation script:', error=True)
raise
finally:
......@@ -204,6 +201,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
# allow to work w/o cache
pass
creds['external_login'] = user
creds['login_portal_type'] = self.login_portal_type
creds['remote_host'] = request.get('REMOTE_HOST', '')
try:
creds['remote_address'] = request.getClientAddr()
......@@ -211,7 +209,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
creds['remote_address'] = request.get('REMOTE_ADDR', '')
return creds
class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin):
class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPluginBase):
"""
Plugin to authenicate as machines.
"""
......@@ -219,6 +217,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi
meta_type = "ERP5 Facebook Extraction Plugin"
prefix = 'fb_'
header_string = 'facebook'
login_portal_type = 'Facebook Login'
def getUserEntry(self, token):
if facebook is None:
......@@ -250,7 +249,7 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi
user_entry = None
return user_entry
class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin):
class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPluginBase):
"""
Plugin to authenicate as machines.
"""
......@@ -258,6 +257,7 @@ class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin)
meta_type = "ERP5 Google Extraction Plugin"
prefix = 'go_'
header_string = 'google'
login_portal_type = 'Google Login'
def getUserEntry(self, token):
if httplib2 is None:
......
......@@ -25,14 +25,13 @@ from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.ERP5Type \
import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery
from ZODB.POSException import ConflictError
import sys
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
# complex security issues related to caching groups. For example,
......@@ -45,8 +44,8 @@ NO_CACHE_MODE = 0
class ConsistencyError(Exception): pass
manage_addERP5GroupManagerForm = PageTemplateFile(
'www/ERP5Security_addERP5GroupManager', globals(),
__name__='manage_addERP5GroupManagerForm' )
'www/ERP5Security_addERP5GroupManager', globals(),
__name__='manage_addERP5GroupManagerForm' )
def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ):
""" Add a ERP5GroupManager to a Pluggable Auth Service. """
......@@ -56,10 +55,10 @@ def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ):
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(
'%s/manage_workspace'
'?manage_tabs_message='
'ERP5GroupManager+added.'
% dispatcher.absolute_url())
'%s/manage_workspace'
'?manage_tabs_message='
'ERP5GroupManager+added.'
% dispatcher.absolute_url())
class ERP5GroupManager(BasePlugin):
......@@ -117,17 +116,10 @@ class ERP5GroupManager(BasePlugin):
else:
security_definition_list = mapping_method()
# get the person from its reference - no security check needed
catalog_result = self.portal_catalog.unrestrictedSearchResults(
portal_type="Person", query=SimpleQuery(reference=user_name))
if len(catalog_result) != 1: # we won't proceed with groups
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()
# get the person from its login - no security check needed
person_object = self.erp5_users.getPersonByReference(user_name)
if person_object is None: # no person is linked to this user login
return ()
# Fetch category values from defined scripts
for (method_name, base_category_list) in security_definition_list:
......
......@@ -46,8 +46,7 @@ from Products.PluggableAuthService.plugins.CookieAuthHelper import CookieAuthHel
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Security.ERP5UserManager import ERP5UserManager, \
SUPER_USER, \
from Products.ERP5Security.ERP5UserManager import SUPER_USER,\
_AuthenticationFailure
from Crypto.Cipher import AES
......@@ -130,8 +129,8 @@ class ILoginEncryptionPlugin(Interface):
#Form for new plugin in ZMI
manage_addERP5KeyAuthPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5KeyAuthPlugin', globals(),
__name__='manage_addERP5KeyAuthPluginForm')
'www/ERP5Security_addERP5KeyAuthPlugin', globals(),
__name__='manage_addERP5KeyAuthPluginForm')
def addERP5KeyAuthPlugin(dispatcher, id, title=None,
encryption_key='', cipher='AES', cookie_name='',
......@@ -150,7 +149,7 @@ def addERP5KeyAuthPlugin(dispatcher, id, title=None,
'ERP5KeyAuthPlugin+added.'
% dispatcher.absolute_url())
class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
class ERP5KeyAuthPlugin(CookieAuthHelper):
"""
Key authentification PAS plugin which support key authentication in URL.
......@@ -237,38 +236,17 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
#Search __ac_key
key = request.get('__ac_key', None)
if key is not None:
creds['key'] = key
creds['external_login'] = self.decrypt(key)
#Save this in cookie
self.updateCredentials(request, request["RESPONSE"], None, None)
else:
# Look in the request for the names coming from the login form
#It's default method
login_pw = request._authUserPW()
if login_pw is not None:
name, password = login_pw
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
#search in cookies
cookie = request.get(self.cookie_name, None)
if cookie is not None:
#Cookie is found
cookie_val = unquote(cookie)
creds['external_login'] = self.decrypt(cookie_val)
#Complete credential with some information
if creds:
......@@ -315,78 +293,15 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
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 #
################################
#'Edit' option form
manage_editERP5KeyAuthPluginForm = PageTemplateFile(
'www/ERP5Security_editERP5KeyAuthPlugin',
globals(),
__name__='manage_editERP5KeyAuthPluginForm' )
'www/ERP5Security_editERP5KeyAuthPlugin',
globals(),
__name__='manage_editERP5KeyAuthPluginForm' )
security.declareProtected( ManageUsers, 'manage_editKeyAuthPlugin' )
def manage_editKeyAuthPlugin(self, encryption_key, cipher, cookie_name,
......
......@@ -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')):
raise RuntimeError('Catalog does not have column information. Make sure RDB is working and disk is not full.')
result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference',
portal_type="Person",
select_expression='reference, portal_type',
portal_type=("ERP5 Login"),
reference=dict(query=login, key=reference_key))
# XXX: Here, we filter catalog result list ALTHOUGH we did pass
# parameters to unrestrictedSearchResults to restrict result set.
......@@ -88,8 +88,17 @@ def getUserByLogin(portal, login, exact_match=True):
# 1 row in set (0.01 sec)
# "bar OR foo" because of ZSQLCatalog tokenizing searched strings
# by default (feature).
return [x.getObject() for x in result if not exact_match
or x['reference'] in login]
result_list = [x.getObject().getParentValue()
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)
def getValidAssignmentList(user):
......@@ -113,6 +122,7 @@ class ERP5UserManager(BasePlugin):
"""
meta_type = 'ERP5 User Manager'
login_portal_type = 'ERP5 Login'
security = ClassSecurityInfo()
......@@ -120,6 +130,52 @@ class ERP5UserManager(BasePlugin):
self._id = self.id = id
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
#
......@@ -143,69 +199,84 @@ class ERP5UserManager(BasePlugin):
return None
@UnrestrictedMethod
def _authenticateCredentials(login, password, path,
def _authenticateCredentials(login, password, portal_type,
ignore_password=False):
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:
raise _AuthenticationFailure()
if not login_object:
raise _AuthenticationFailure(None)
user = user_list[0]
if login_object.getPortalType() == 'Person':
# BBB
user = login_object
else:
user = login_object.getParentValue()
try:
if (ignore_password or pw_validate(user.getPassword(), password)) and \
len(getValidAssignmentList(user)) and user \
.getValidationState() != 'deleted': #user.getCareerRole() == 'internal':
return login, login # use same for user_id and login
if self.checkPersonValidity(user) and \
(ignore_password or self._validatePassword(login_object, password)):
return user.getReference(), login_object.getRelativeUrl()
finally:
pass
raise _AuthenticationFailure()
raise _AuthenticationFailure(login_object.getRelativeUrl())
_authenticateCredentials = CachingMethod(
_authenticateCredentials,
id='ERP5UserManager_authenticateCredentials',
id=self.__class__.__name__ + '_authenticateCredentials',
cache_factory='erp5_content_short')
try:
authentication_result = _authenticateCredentials(
user_reference, login_url = _authenticateCredentials(
login=login,
password=credentials.get('password'),
path=self.getPhysicalPath(),
portal_type=credentials.get('login_portal_type',
self.login_portal_type),
ignore_password=ignore_password)
except _AuthenticationFailure, exception:
user_reference = None
login_url = exception.message or None
except _AuthenticationFailure:
authentication_result = None
if user_reference and '_login_cache' not in self.REQUEST:
self.REQUEST.set('_login_cache', {})
self.REQUEST['_login_cache'][user_reference] = login_url
if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled():
# stop here, no authentication policy enabled
# 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
user_list = self.getUserByLogin(credentials.get('login'))
if not user_list:
# not an ERP5 Person object
if login_url is 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
user.notifyLoginFailure()
login.notifyLoginFailure()
return None
# check if password is expired
if user.isPasswordExpired():
user.notifyPasswordExpire()
if login.isPasswordExpired():
login.notifyPasswordExpire()
return None
# check if user account is blocked
if user.isLoginBlocked():
# check if login is blocked
if login.isLoginBlocked():
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
......@@ -215,7 +286,7 @@ class ERP5UserManager(BasePlugin):
sort_by=None, max_results=None, **kw):
""" See IUserEnumerationPlugin.
"""
if id is None:
if not id:
id = login
if isinstance(id, str):
id = (id,)
......@@ -228,25 +299,70 @@ class ERP5UserManager(BasePlugin):
id_list = []
for user_id in id:
if SUPER_USER == user_id:
info = { 'id' : SUPER_USER
, 'login' : SUPER_USER
, 'pluginid' : plugin_id
}
info = {'id' : SUPER_USER,
'login' : SUPER_USER,
'pluginid' : plugin_id,
}
user_info.append(info)
else:
id_list.append(user_id)
if id_list:
for user in self.getUserByLogin(tuple(id_list), exact_match=exact_match):
info = { 'id' : user.getReference()
, 'login' : user.getReference()
, 'pluginid' : plugin_id
}
user_info.append(info)
if exact_match:
for reference in id_list:
user = self.getPersonByReference(reference)
if user is not None:
info = {'id': reference,
'login' : reference,
'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)
@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):
# Search the Catalog for login and return a list of person objects
# login can be a string or a list of strings
......
......@@ -49,7 +49,7 @@ class TestUserManagement(ERP5TypeTestCase):
def getBusinessTemplateList(self):
"""List of BT to install. """
return ('erp5_base',)
return ('erp5_base', 'erp5_administration',)
def beforeTearDown(self):
"""Clears person module and invalidate caches when tests are finished."""
......@@ -94,10 +94,11 @@ class TestUserManagement(ERP5TypeTestCase):
newSecurityManager(None, user)
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
indexing is done. """
person_module = self.getPersonModule()
password = kw.pop('password', None)
new_person = person_module.newContent(
portal_type='Person', **kw)
assignment = new_person.newContent(portal_type = 'Assignment',
......@@ -105,7 +106,14 @@ class TestUserManagement(ERP5TypeTestCase):
stop_date=assignment_stop_date,)
if open_assignment:
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
def _assertUserExists(self, login, password):
......@@ -303,9 +311,21 @@ class TestUserManagement(ERP5TypeTestCase):
self.tic()
self._assertUserExists('the_user', 'secret')
self.loginAsUser('the_user')
self.portal.REQUEST.set('current_password', 'secret')
self.portal.REQUEST.set('new_password', 'new_secret')
self.portal.portal_preferences.PreferenceTool_setNewPassword()
login = [x for x in pers.objectValues(portal_type='ERP5 Login')][0]
result = 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._assertUserDoesNotExists('the_user', 'secret')
......@@ -327,8 +347,14 @@ class TestUserManagement(ERP5TypeTestCase):
self._assertUserDoesNotExists('the_user', 'secret')
def test_PersonNotIndexedNotCached(self):
pers = self._makePerson(password='secret',)
pers = self._makePerson()
pers.setReference('the_user')
login = pers.newContent(
portal_type='ERP5 Login',
reference='the_user',
password='secret',
)
login.validate()
# not indexed yet
self._assertUserDoesNotExists('the_user', 'secret')
......@@ -339,9 +365,27 @@ class TestUserManagement(ERP5TypeTestCase):
def test_PersonNotValidNotCached(self):
pers = self._makePerson(reference='the_user', password='other',)
self._assertUserDoesNotExists('the_user', 'secret')
pers.setPassword('secret')
login = pers.objectValues(portal_type='ERP5 Login')[0]
login.setPassword('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):
"""Tests a person with an assignment with correct date is a valid user."""
......@@ -402,6 +446,32 @@ class TestUserManagement(ERP5TypeTestCase):
self.tic()
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):
def getTitle(self):
"""Title of the test."""
......@@ -484,12 +554,9 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
base_cat.newContent(portal_type='Category',
id='subcat',
codification="%s1" % code)
# add another function subcategory.
function_category = category_tool['function']
if function_category.get('another_subcat', None) is None:
function_category.newContent(portal_type='Category',
id='another_subcat',
codification='F2')
base_cat.newContent(portal_type='Category',
id='another_subcat',
codification="%s2" % code)
self.defined_category = "group/subcat\n"\
"site/subcat\n"\
"function/subcat"
......@@ -543,7 +610,7 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
def getBusinessTemplateList(self):
"""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):
"""Tests group manager plugin respects interfaces."""
......@@ -575,6 +642,30 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
self.assertEqual(['Assignor'], obj.__ac_local_roles__.get('F1_G1_S1'))
self.assertTrue('Assignor' 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()
def testLocalRolesGroupId(self):
......
......@@ -305,7 +305,7 @@ class ERP5TypeInformation(XMLObject,
# Document related to a person's assignment or career step
'personal_item',
# Base
'entity',
'entity', 'login',
# LEGACY - needs a warning - XXX-JPS
'tax_movement',
)
......
......@@ -429,8 +429,11 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
person = self.portal.person_module.newContent(portal_type='Person',
reference=reference,
password=password,
**person_kw)
login = person.newContent(portal_type='ERP5 Login',
reference=reference,
password=password)
login.validate()
return person
def createUserAssignment(self, user, assignment_kw):
......
......@@ -55,10 +55,14 @@ class ERP5RemoteUserManager(ERP5UserManager):
"""
meta_type = 'ERP5 Remote User Manager'
login_portal_type = 'ERP5 Remote Login'
security = ClassSecurityInfo()
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
# Thanks to this it is possible to login to instance, even
# if master authentication server is down
......@@ -69,6 +73,7 @@ class ERP5RemoteUserManager(ERP5UserManager):
#
# any other error is assumed as fatal and results in disallowing
# authentication and clearing local cache
login = login_object.getReference()
if self.remote_authentication_cache is None:
self.remote_authentication_cache = OOBTree()
portal = self.getPortalObject()
......@@ -122,70 +127,6 @@ class ERP5RemoteUserManager(ERP5UserManager):
del self.remote_authentication_cache[login]
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
, IAuthenticationPlugin
, 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