diff --git a/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py b/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py index 0455e0e6049308d26d4f82c99bc164ec27fbf9d3..db2fed9f3181c8dc4e03332faed88bb27029c82e 100644 --- a/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py +++ b/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py @@ -1,7 +1,8 @@ -alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' -random_id = '' -for _ in range(0, 128): - random_id += random.choice(alpha) +import os +import base64 + +# This is python 3.6 secret.token_urlsafe +random_id = base64.urlsafe_b64encode(os.urandom(48)).rstrip(b'=').decode('ascii') # Define Reference from ID provided by portal_ids portal = context.getPortalObject() diff --git a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py index 39b6647808afc510f91dcd7c54a2c349d9985042..bcaa50e208882633d55637e7b6680301d17c89df 100644 --- a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py +++ b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py @@ -1,50 +1 @@ -""" - This script generates a human readable random - password in the form 'word'+digits+'word'. - - from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410076 - - parameters: number of 'characters' , number of 'digits' - Pradeep Kishore Gowda - License : GPL - Date : 2005.April.15 - Revision 1.2 - ChangeLog: - 1.1 - fixed typos - 1.2 - renamed functions _apart & _npart to a_part & n_part as zope does not allow functions to - start with _ -""" - -import string, random -vowels = ['a','e','i','o','u'] -consonants = [a for a in string.ascii_lowercase if a not in vowels] -digits = string.digits - -def a_part(slen): - ret = '' - for i in range(slen): - if i%2 ==0: - randid = random.randint(0,20) #number of consonants - ret += consonants[randid] - else: - randid = random.randint(0,4) #number of vowels - ret += vowels[randid] - return ret - -def n_part(slen): - ret = '' - for i in range(slen): - randid = random.randint(0,9) #number of digits - ret += digits[randid] - return ret - -fpl = alpha/2 -if alpha % 2 : - fpl = int(alpha/2) + 1 -lpl = alpha - fpl - -start = a_part(fpl) -mid = n_part(numeric) -end = a_part(lpl) -newpass = "%s%s%s" % (start,mid,end) -return newpass +return ''.join(random.SystemRandom().sample(string.letters + string.digits, length)) diff --git a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml index ee98b10c6dae41672fde959dafe01d01b4addd59..3ad714f2d5f8eb2ca425ceb857df1068e50da94e 100644 --- a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml +++ b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml @@ -50,7 +50,7 @@ _params - alpha=6, numeric=2 + length=20 id @@ -58,7 +58,7 @@ title - Generate human readable random password + diff --git a/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py b/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py index adc98c2d042a53489fa67451a38a51fe6d8b87a8..ced491fa6cf7808553b02e82771e7e2f881cc96a 100644 --- a/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py +++ b/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py @@ -52,7 +52,7 @@ if not login.hasPassword(): credential_recovery.submit() else: # system should generate a password - password = context.Person_generatePassword(alpha=5, numeric=3) + password = context.Person_generatePassword() login.setPassword(password) # create a global account diff --git a/product/ERP5Type/patches/Restricted.py b/product/ERP5Type/patches/Restricted.py index 2350f61a844f2a96b8b84320e9c0ecfa7ca20def..0af676a3c5a00270079bb56be2d9b8156d134c6a 100644 --- a/product/ERP5Type/patches/Restricted.py +++ b/product/ERP5Type/patches/Restricted.py @@ -344,3 +344,7 @@ for member_id in dir(decimal): if isinstance(member, type) and issubclass(member, decimal.DecimalException): ContainerAssertions[member] = 1 del member_id, member + +from random import SystemRandom +allow_type(SystemRandom) +ModuleSecurityInfo('os').declarePublic('urandom') diff --git a/product/ERP5Type/tests/testRestrictedPythonSecurity.py b/product/ERP5Type/tests/testRestrictedPythonSecurity.py index ff29c15958fe8691d39b3959592803b555b8822b..227efde8d3f18655c45b2cbe46837cca77d70df5 100644 --- a/product/ERP5Type/tests/testRestrictedPythonSecurity.py +++ b/product/ERP5Type/tests/testRestrictedPythonSecurity.py @@ -25,8 +25,6 @@ # ############################################################################## -import unittest - from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.utils import createZODBPythonScript from Products.ERP5Type.tests.utils import removeZODBPythonScript @@ -123,3 +121,19 @@ class TestRestrictedPythonSecurity(ERP5TypeTestCase): 'return urlparse.parse_qsl("q=s")', expected=[('q', 's')] ) + + def testSystemRandom(self): + self.createAndRunScript('import random', + 'return random.SystemRandom().getrandbits(10)') + + def test_os_urandom(self): + self.createAndRunScript('import os', + 'return os.urandom(10)') + # other "unsafe" os members are not exposed + self.assertRaises(Unauthorized, + self.createAndRunScript, 'import os', + 'return os.path.exists("/")') + self.assertRaises(Unauthorized, + self.createAndRunScript, 'import os', + 'return os.system') +