Commit 73e08c0b authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki Committed by Cédric Le Ninivin

ERP5Security: Use a dedicated Login document to handle authentication.

parent c064d05e
<workflow_chain>
<chain>
<type>Person</type>
<type>ERP5 Login</type>
<workflow>password_interaction_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
......@@ -160,7 +160,7 @@ return result_code_list\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_analyzePassword</string> </value>
<value> <string>Login_analyzePassword</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -62,7 +62,7 @@ return brain.url\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getListboxUrl</string> </value>
<value> <string>Login_getListboxUrl</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -121,7 +121,7 @@ return 0\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_isLoginBlocked</string> </value>
<value> <string>Login_isLoginBlocked</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -53,7 +53,7 @@
<value> <string encoding="cdata"><![CDATA[
"""\n
Returns if user account is Person\'s password is expired.\n
Returns if user account Login\'s password is expired.\n
Start password recovery process for expired password (if configured).\n
"""\n
from Products.ERP5Type.Cache import CachingMethod\n
......@@ -88,7 +88,7 @@ def _isPasswordExpired():\n
return False, expire_date_warning\n
\n
_isPasswordExpired = CachingMethod(_isPasswordExpired,\n
id=\'Person_isPasswordExpired_%s\' %context.getReference(),\n
id=\'%s_%s\' % (script.getId(), context.getReference()),\n
cache_factory=\'erp5_content_short\')\n
is_password_expired, expire_date = _isPasswordExpired()\n
\n
......@@ -114,7 +114,7 @@ return is_password_expired\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_isPasswordExpired</string> </value>
<value> <string>Login_isPasswordExpired</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -65,30 +65,16 @@ message_dict = { 0: \'Unknown error\',\n
-4: \'You have already used this password.\',\n
-5: \'You can not use any parts of your first and last name in password.\'}\n
\n
def doValidation(person, password):\n
def doValidation(login, password):\n
# raise so Formulator shows proper message\n
result_code_list = person.Person_analyzePassword(password)\n
result_code_list = login.analyzePassword(password)\n
if result_code_list!=[]:\n
translateString = context.Base_translateString\n
message = \' \'.join([translateString(message_dict[x]) for x in result_code_list])\n
raise ValidationError(\'external_validator_failed\', context, error_text=message)\n
return 1\n
\n
user_login = request.get(\'field_user_login\', None)\n
# find Person object (or authenticated member) and validate it on it (password recovered for an existing account)\n
person = context.ERP5Site_getAuthenticatedMemberPersonValue(user_login)\n
if person is not None:\n
return doValidation(person, password)\n
\n
# use a temp object (new account created)\n
first_name = request.get(\'field_your_first_name\', None) \n
last_name = request.get(\'field_your_last_name\', None) \n
kw = {\'title\': \'%s %s\' %(first_name, last_name),\n
\'first_name\': first_name,\n
\'last_name\': last_name}\n
person = newTempBase(portal, kw[\'title\'], **kw)\n
\n
return doValidation(person, password)\n
return doValidation(context, password)\n
</string> </value>
</item>
<item>
......@@ -105,7 +91,7 @@ return doValidation(person, password)\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isPasswordValid</string> </value>
<value> <string>Login_isPasswordValid</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -85,7 +85,7 @@ return authentication_event\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_notifyLoginFailure</string> </value>
<value> <string>Login_notifyLoginFailure</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -107,7 +107,7 @@ credential_recovery.submit()\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_notifyPasswordExpire</string> </value>
<value> <string>Login_notifyPasswordExpire</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -96,7 +96,7 @@ return\n
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_unblockLogin</string> </value>
<value> <string>Login_unblockLogin</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -77,20 +77,21 @@ kw = {\'portal_type\': \'Authentication Event\',\n
\'validation_state\' : \'confirmed\'}\n
failure_list = portal.portal_catalog(**kw)\n
for failure in failure_list:\n
person = failure.getDestinationValue()\n
if person not in all_blocked_user_login_dict.keys():\n
all_blocked_user_login_dict[person] = []\n
all_blocked_user_login_dict[person].append(failure)\n
login = failure.getDestinationValue()\n
if login not in all_blocked_user_login_dict.keys():\n
all_blocked_user_login_dict[login] = []\n
all_blocked_user_login_dict[login].append(failure)\n
\n
# leave only ones that are blocked:\n
for person, failure_list in all_blocked_user_login_dict.items():\n
for login, failure_list in all_blocked_user_login_dict.items():\n
if len(failure_list) >= max_authentication_failures:\n
title = login.getParentTitle()\n
blocked_user_login_list.append(newTempBase(portal, \n
person.getTitle(), \n
**{\'title\': person.getTitle(),\n
title, \n
**{\'title\': title,\n
\'count\':len(failure_list),\n
\'reference\': person.getReference(),\n
\'url\': person.absolute_url()}))\n
\'reference\': login.getReference(),\n
\'url\': login.absolute_url()}))\n
return blocked_user_login_list\n
......
......@@ -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>
......
......@@ -50,21 +50,20 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>from DateTime import DateTime\n
portal = context.getPortalObject()\n
person = state_change[\'object\']\n
<value> <string>login = state_change[\'object\']\n
portal = login.getPortalObject()\n
\n
# check preferences and save only if set\n
number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck()\n
\n
if number_of_last_password_to_check is not None and number_of_last_password_to_check:\n
# save password and modification date\n
current_password = person.getPassword()\n
current_password = login.getPassword()\n
if current_password is not None:\n
password_event = portal.system_event_module.newContent(portal_type = \'Password Event\',\n
source_value = person,\n
destination_value = person,\n
password = current_password)\n
password_event = portal.system_event_module.newContent(portal_type=\'Password Event\',\n
source_value=login,\n
destination_value=login,\n
password=current_password)\n
password_event.confirm()\n
# Person_isPasswordExpired cache the wrong result if document is not in catalog.\n
# As the document is created in the same transaction, it is possible to force reindexation\n
......@@ -86,7 +85,7 @@ if number_of_last_password_to_check is not None and number_of_last_password_to_c
</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>
......
......@@ -27,6 +27,7 @@
##############################################################################
import erp5
import re
from Products.ERP5.Extensions.CheckPortalTypes import changeObjectClass
def migrateToEmbeddedFile(self, force=0):
......@@ -37,7 +38,50 @@ def migrateToEmbeddedFile(self, force=0):
if portal_type in ('File', 'Image') and self.getValidationState()=='embedded':
embedded_type = 'Embedded File'
container = self.getParentValue()
id = self.id
if force == 1:
changeObjectClass(container, id, getattr(erp5.portal_type, embedded_type))
changeObjectClass(container, self.id, getattr(erp5.portal_type, embedded_type))
return '%s: %s -> %s' % (self.getRelativeUrl(), portal_type, embedded_type),
def migrateToLogin(self):
assert self.getPortalType() == 'Person'
if len(self.objectValues(portal_type=self.getPortalObject().getPortalLoginTypeList())):
# already migrated
return
reference = self.getReference()
if not reference:
# no login is required
return
login_list = []
if re.match(r'^fb_\d+$', reference):
login = self.newContent(
portal_type='Facebook Login',
reference=reference,
)
login_list.append(login)
elif re.match(r'^go_\d+$', reference):
login = self.newContent(
portal_type='Google Login',
reference=reference,
)
login_list.append(login)
elif re.match('^bid_[^@]+@[^@]+$', reference):
login = self.newContent(
portal_type='Persona Login',
reference=reference,
)
login_list.append(login)
if self.hasPassword():
login = self.newContent(
portal_type='ERP5 Login',
reference=reference,
)
login._setEncodedPassword(self.getPassword())
login_list.append(login)
if not login_list:
login = self.newContent(
portal_type='ERP5 Login',
reference=reference,
)
login_list.append(login)
for login in login_list:
login.validate()
......@@ -6,10 +6,22 @@
</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>BaseMigration</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.BaseMigration</string> </value>
......@@ -24,6 +36,18 @@
<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>
......@@ -31,13 +55,28 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<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>
......@@ -50,7 +89,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -59,7 +98,7 @@
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
......
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>
......@@ -105,6 +105,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>
......@@ -123,4 +124,4 @@
<portal_type id="Solver Tool">
<item>Solver Type</item>
</portal_type>
</allowed_content_type_list>
</allowed_content_type_list>
\ No newline at end of file
......@@ -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>
......@@ -35,6 +35,10 @@
<type>Currency Exchange Line</type>
<workflow>currency_exchange_line_interaction_workflow, edit_workflow, validation_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>
<?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>_body</string> </key>
<value> <string encoding="cdata"><![CDATA[
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, ComplexQuery\n
query = ComplexQuery(\n
SimpleQuery(portal_type=portal_type),\n
SimpleQuery(reference=reference),\n
)\n
login_list = [x for x in context.getPortalObject().portal_catalog(\n
select_list=(\'reference\',),\n
query=query)\n
if x.reference == reference]\n
if ignore_uid is not None:\n
login_list = [x for x in login_list if x.uid != ignore_uid]\n
return len(login_list) > 0\n
]]></string> </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>
......@@ -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>
<?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>_body</string> </key>
<value> <string>portal = context.getPortalObject()\n
result = []\n
sql_result = portal.ERP5Site_zGetDuplicateLoginReferenceList()\n
if len(sql_result):\n
from Products.CMFActivity.ActiveResult import ActiveResult\n
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, ComplexQuery\n
query = ComplexQuery(\n
*[ComplexQuery(SimpleQuery(portal_type=i[\'portal_type\']),\n
SimpleQuery(reference=i[\'reference\'])) for i in sql_result],\n
operator=\'OR\')\n
active_result = ActiveResult()\n
for i in portal.portal_catalog(\n
select_list=(\'portal_type\', \'reference\',),\n
query=query):\n
result.append(\'%r, %r, %r\' % (i[\'portal_type\'], i[\'reference\'], i[\'path\']))\n
active_result.edit(summary=\'Logins having the same reference exist\',\n
severity=len(sql_result),\n
detail=\'\\n\'.join(result))\n
active_process = context.newActiveProcess()\n
active_process.postResult(active_result)\n
return active_process\n
</string> </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>
<?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>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
SELECT\n
reference, portal_type\n
FROM\n
catalog\n
WHERE\n
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>)\n
AND validation_state=\'validated\'\n
GROUP BY\n
reference, portal_type\n
HAVING\n
count(*) > 1\n
]]></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>
<?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>_body</string> </key>
<value> <string>return context.getPortalObject().Base_checkLoginExistence(\n
portal_type=context.getPortalType(),\n
reference=context.getReference(),\n
ignore_uid=context.getUid(),\n
)\n
</string> </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>
<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>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>The username this person will use to log in the system.\n
\n
The system will check that there isn\'t another user with the same username.</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_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>
<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>
<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(\'Manage users\', 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>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>
<?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>_body</string> </key>
<value> <string>error_list = []\n
if context.hasReference() and \\\n
not len(context.objectValues(portal_type=context.getPortalObject().getPortalLoginTypeList())):\n
error_list.append(\'%s has no Login type sub document.\' % context.getRelativeUrl())\n
if fixit:\n
context.Person_migrateToLogin()\n
return error_list\n
</string> </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>
<?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>_body</string> </key>
<value> <string>def migrateLogin():\n
login = context.newContent(\n
portal_type=\'Login\',\n
reference=context.getReference(),\n
password=context.getPassword(),\n
)\n
login.validate()\n
return login\n
\n
login_list = context.objectValues(portal_type=\'Login\')\n
\n
if not login_list:\n
return [migrateLogin()]\n
elif not [x for x in login_list if x.getValidationState() == \'validated\']:\n
return [migrateLogin()] + login_list\n
else:\n
return login_list\n
</string> </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>
<?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>_body</string> </key>
<value> <string>object = state_change[\'object\']\n
object.Base_checkConsistency()\n
</string> </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>
<?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>_body</string> </key>
<value> <string>obj = state_change[\'object\']\n
obj.activate(tag=\'login:%s:%s\' % (obj.getPortalType(), obj.getReference())).reindexObject()\n
</string> </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>
<?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>_body</string> </key>
<value> <string>obj = state_change[\'object\']\n
for plugin in obj.getPortalObject().acl_users.objectValues():\n
if getattr(plugin, \'getLoginPortalType\', lambda: None)() == obj.getPortalType():\n
plugin.unregisterLogin(obj)\n
</string> </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>
<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="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_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>
<none/>
</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>
</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>
<none/>
</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>
</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>
<none/>
</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>
</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>
......@@ -24,6 +24,7 @@ Delivery Builder | view_predicate_group
Delivery Builder | view_profile
Delivery Causality Assignment Movement Group | view
Delivery Tool | view
ERP5 Login | view
Email | change_function
Email | view
Embedded File | download
......@@ -99,4 +100,4 @@ Title Movement Group | view
Variant Movement Group | view
Variation Property Movement Group | view
portal_actions | jump_query
portal_actions | post_query
portal_actions | post_query
\ 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
......@@ -74,6 +74,7 @@ Person | Bank Account
Person | Career
Person | Chat Address
Person | Credit Card
Person | ERP5 Login
Person | Email
Person | Embedded File
Person | Fax
......@@ -81,4 +82,4 @@ Person | Link
Person | Telephone
Query Module | Query
Rounding Tool | Rounding Model
Solver Tool | Solver Type
Solver Tool | Solver Type
\ No newline at end of file
......@@ -18,6 +18,7 @@ Day Movement Group
Delivery Builder
Delivery Causality Assignment Movement Group
Delivery Tool
ERP5 Login
Email
Embedded File
Embedded Folder
......@@ -61,4 +62,4 @@ Split Movement Group
Telephone
Title Movement Group
Variant Movement Group
Variation Property Movement Group
Variation Property Movement Group
\ No newline at end of file
......@@ -2,6 +2,7 @@ Notification Message | ItemAggregation
Notification Message | Reference
Organisation | DefaultImage
Person | DefaultImage
Person | PersonUpgradeConstraint
Preference | TelephonePreference
Previous Causality Movement Group | PreviousCausalityMovementGroup
Query | TextDocument
\ No newline at end of file
......@@ -13,6 +13,8 @@ Currency Exchange Line | edit_workflow
Currency Exchange Line | validation_workflow
Currency | edit_workflow
Currency | validation_workflow
ERP5 Login | edit_workflow
ERP5 Login | login_validation_workflow
Email | edit_workflow
Embedded File | document_conversion_interaction_workflow
Embedded File | 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.
......
......@@ -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>
......
##############################################################################
#
# 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', ))
......@@ -1516,6 +1516,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):
......
......@@ -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'}):
......
<?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>_body</string> </key>
<value> <string>person = context.ERP5Site_getAuthenticatedMemberPersonValue()\n
if person is None:\n
return []\n
else:\n
return [(x.getReference(), x.getRelativeUrl()) for x in \\\n
person.objectValues(portal_type=\'ERP5 Login\') \\\n
if x.getValidationState() == \'validated\']\n
</string> </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>
......@@ -53,28 +53,26 @@
<value> <string>from AccessControl import getSecurityManager\n
from Products.ERP5Type.Message import translateString\n
\n
request = context.REQUEST\n
new_password = request.get("new_password")\n
former_password = request.get("current_password")\n
\n
portal = context.getPortalObject()\n
user = getSecurityManager().getUser()\n
persons = context.acl_users.erp5_users.getUserByLogin(user)\n
person = persons[0]\n
\n
if not person.checkPassword(former_password):\n
person = context.acl_users.erp5_users.getPersonByReference(user.getId())\n
login = portal.restrictedTraverse(login)\n
if login.getParentValue() != person:\n
msg = translateString(\'You can change your own password only.\')\n
return context.Base_redirect(dialog_id, keep_items=dict(portal_status_message=msg))\n
elif not login.checkPassword(current_password):\n
msg = translateString("Current password is wrong.")\n
else:\n
msg = translateString("Password changed.")\n
person.setPassword(new_password)\n
return context.Base_redirect(dialog_id, keep_items=dict(portal_status_message=msg))\n
\n
login.setPassword(new_password)\n
# clear erp5_content_short cache (see _authenticateCredentials in Products.ERP5Security.ERP5UserManager)\n
context.portal_caches.clearCache((\'erp5_content_short\',))\n
return context.logout()\n
return portal.Base_redirect(\'logout\')\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>form_id=\'view\', **kw</string> </value>
<value> <string>new_password, current_password, login, dialog_id=\'view\', **kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -87,7 +87,7 @@
<key> <string>left</string> </key>
<value>
<list>
<string>your_reference</string>
<string>your_login</string>
<string>your_current_password</string>
<string>your_new_password</string>
<string>password_confirm</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_login</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>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_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_getValidatedLoginItemList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -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")))
......
......@@ -1108,6 +1108,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,
......
......@@ -97,6 +97,7 @@ class ERP5UserManager(BasePlugin):
"""
meta_type = 'ERP5 User Manager'
login_portal_type = 'ERP5 Login'
security = ClassSecurityInfo()
......@@ -105,6 +106,53 @@ 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('_login_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',),
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:
return person_list[0]['relative_url']
_getPersonRelativeUrlFromReference = CachingMethod(
_getPersonRelativeUrlFromReference,
id='ERP5UserManager._getPersonRelativeUrlFromReference',
cache_factory='erp5_content_short')
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
#
......@@ -128,82 +176,80 @@ 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:
# 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.hasStopDate() and \
assignment.getStopDate() < login_date:
continue
valid_assignment_list.append(assignment)
if (ignore_password or pw_validate(user.getPassword(), password)) and \
len(valid_assignment_list) and user \
.getValidationState() != 'deleted': #user.getCareerRole() == 'internal':
return login, login # use same for user_id and login
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 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
......@@ -213,7 +259,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,)
......@@ -226,25 +272,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
......
......@@ -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',
login=login.getRelativeUrl(),
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',
login=login.getRelativeUrl(),
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')
......@@ -325,8 +345,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')
......@@ -337,9 +363,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."""
......@@ -400,6 +444,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."""
......
......@@ -319,7 +319,7 @@ class ERP5TypeInformation(XMLObject,
# Module
'module',
# Base
'entity',
'entity', 'login',
# LEGACY - needs a warning - XXX-JPS
'tax_movement',
)
......
......@@ -422,8 +422,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