From 30eb19ed95d017be8bde76c3330b84f6db06c4ce Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 5 Mar 2020 17:01:06 +0100 Subject: [PATCH 01/52] Fix tests, add to DIRACCA mathod to create fake proxy --- Core/Security/ProxyInfo.py | 7 +- .../ProxyProvider/DIRACCAProxyProvider.py | 65 +++++-- tests/Integration/Framework/Test_ProxyDB.py | 158 +++++++++++------- 3 files changed, 153 insertions(+), 77 deletions(-) diff --git a/Core/Security/ProxyInfo.py b/Core/Security/ProxyInfo.py index 3cdbcd26ba0..fcf56b5eac2 100644 --- a/Core/Security/ProxyInfo.py +++ b/Core/Security/ProxyInfo.py @@ -6,7 +6,7 @@ import six import base64 -from DIRAC import S_OK, S_ERROR +from DIRAC import S_OK, S_ERROR, gLogger from DIRAC.Core.Utilities import DErrno from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Core.Security.VOMS import VOMS @@ -157,7 +157,10 @@ def formatProxyStepsInfoAsString(infoList): if key in stepInfo: value = stepInfo[key] if key == 'serial': - value = base64.b16encode(value) + try: + value = base64.b16encode(value) + except Exception as e: + gLogger.error('Cannot read serial:', e) if key == 'lifetime': secs = value hours = int(secs / 3600) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 510d70cb2e1..d354d2a9ddf 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -51,11 +51,15 @@ def __init__(self, parameters=None): if nid not in self.n2field: self.n2field[nid] = field self.n2field[nid] = len(field) < len(self.n2field[nid]) and field or self.n2field[nid] + self.caDict = {} + self.caFieldByNid = {} def setParameters(self, parameters): """ Set new parameters :param dict parameters: provider parameters + + :return: S_OK()/S_ERROR() """ # If CA configuration file exist self.parameters = parameters @@ -78,6 +82,7 @@ def setParameters(self, parameters): self.optional = [] for field in parameters['Optional'].replace(' ', '').split(','): self.optional.append(self.fs2nid[field]) + # Set defaults for distridutes names self.defDict = {} for field, value in parameters.items(): @@ -179,8 +184,8 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): if not result['OK']: return result caDN = result['Value']['subject'] - caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) - caFieldByNid = dict([[self.fs2nid[field], field] for field in caDict]) + self.caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) + self.caFieldByNid = dict([[self.fs2nid[field], field] for field in self.caDict]) dnDict = {} if userDN: @@ -193,9 +198,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): for nid in dnFieldByNid: if nid not in self.supplied + self.match + self.optional: return S_ERROR('Current DN is invalid, "%s" field is not found for current CA.' % dnFieldByNid[nid]) - if nid in self.match and not caDict[caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: + if nid in self.match and not self.caDict[self.caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: return S_ERROR('Current DN is invalid, "%s" field must be %s.' % (dnFieldByNid[nid], - caDict[caFieldByNid[nid]])) + self.caDict[self.caFieldByNid[nid]])) if nid in self.maxDict and len(dnDict[dnFieldByNid[nid]]) > self.maxDict[nid]: return S_ERROR('Current DN is invalid, "%s" field must be less then %s.' % (dnDict[dnFieldByNid[nid]], self.maxDict[nid])) @@ -223,9 +228,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): self.log.info('Creating distributes names chain') # Test match fields for nid in self.match: - if nid not in caFieldByNid: + if nid not in self.caFieldByNid: return S_ERROR('Distributes name(%s) must be present in CA certificate.' % ', '.join(self.n2fields[nid])) - result = self.__fillX509Name(caFieldByNid[nid], caDict[caFieldByNid[nid]]) + result = self.__fillX509Name(self.caFieldByNid[nid], self.caDict[self.caFieldByNid[nid]]) if not result['OK']: return result # Test supplied fields @@ -243,8 +248,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): # WARN: This logic not support list of distribtes name elements resDN = m2.x509_name_oneline(self.__X509Name.x509_name) # pylint: disable=no-member + if userDN and not userDN == resDN: - return S_ERROR('%s not match with generated DN: %s' % (userDN, resDN)) + return S_ERROR('%s not matched with created %s' % (userDN, resDN)) return S_OK(resDN) def __parseCACFG(self): @@ -294,8 +300,8 @@ def __parseCACFG(self): def __fillX509Name(self, field, value): """ Fill x509_Name object by M2Crypto - :param basestring field: DN field name - :param basestring value: value of field + :param str field: DN field name + :param str value: value of field :return: S_OK()/S_ERROR() """ @@ -306,10 +312,12 @@ def __fillX509Name(self, field, value): return S_ERROR('Cannot set "%s" field.' % field) return S_OK() - def __createCertM2Crypto(self): + def __createCertM2Crypto(self, extensions=None): """ Create new certificate for user - - :return: S_OK(basestring, basestring)/S_ERROR() + + :param list extensions: list of X509.new_extension() + + :return: S_OK(tuple)/S_ERROR() -- tuple contain certificate and pulic key as strings """ # Create publik key userPubKey = EVP.PKey() @@ -320,9 +328,11 @@ def __createCertM2Crypto(self): userCert.set_version(2) userCert.set_subject(self.__X509Name) userCert.set_serial_number(int(random.random() * 10 ** 10)) - # Add extentionals + # Add extensions userCert.add_ext(X509.new_extension('basicConstraints', 'CA:' + str(False).upper())) userCert.add_ext(X509.new_extension('extendedKeyUsage', 'clientAuth', critical=1)) + for ext in extensions or []: + userCert.add_ext(ext) # Set livetime validityTime = datetime.timedelta(days=400) notBefore = ASN1.ASN1_UTCTIME() @@ -347,3 +357,32 @@ def __createCertM2Crypto(self): userCertStr = userCert.as_pem() userPubKeyStr = userPubKey.as_pem(cipher=None, callback=util.no_passphrase_callback) return S_OK((userCertStr, userPubKeyStr)) + + def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): + """ Get fake proxy for tests + :param str dn: fake DN + :param int time: expired time in a seconds + :param str vo: fake VOMS VO name + :param str group: if need to add fake DIRAC group + :param dict kwargs: fake VO description dictionary with possible fields: + - Expired time + - Role, etc.. + :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string + """ + self.__X509Name = X509.X509_Name() + for field, value in [field.split('=') for field in dn.lstrip('/').split('/')]: + result = self.__fillX509Name(field, value) + if not result['OK']: + return result + + result = self.__createCertM2Crypto([X509.new_extension('vomsExtensions', vo)] if vo else []) + if result['OK']: + certStr, keyStr = result['Value'] + chain = X509Chain() + if chain.loadChainFromString(certStr)['OK'] and chain.loadKeyFromString(keyStr)['OK']: + result = chain.generateProxyToString(time, rfc=True, diracGroup=group) + if not result['OK']: + return result + chain = X509Chain() + chain.loadProxyFromString(result['Value']) + return S_OK((chain, result['Value'])) \ No newline at end of file diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index cfd6ce76047..0098b501972 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -19,6 +19,7 @@ from DIRAC.Core.Utilities.CFG import CFG from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.FrameworkSystem.DB.ProxyDB import ProxyDB +from DIRAC.Resources.ProxyProvider.DIRACCAProxyProvider import DIRACCAProxyProvider # For Jenkins for f in ['', 'TestCode', os.environ['DIRAC']]: @@ -26,6 +27,10 @@ if os.path.exists(certsPath): break +ca = DIRACCAProxyProvider() +ca.setParameters({'CertFile': os.path.join(certsPath, 'ca/ca.cert.pem'), + 'KeyFile': os.path.join(certsPath, 'ca/ca.key.pem')}) + diracTestCACFG = """ Resources { @@ -128,12 +133,12 @@ class ProxyDBTestCase(unittest.TestCase): @classmethod - def createProxy(self, userName, group, time, rfc=True, limit=False, vo=None, role=None, path=None): + def createProxy(self, userName, group, time, vo=None, role=None): """ Create user proxy """ userCertFile = os.path.join(self.userDir, userName + '.cert.pem') userKeyFile = os.path.join(self.userDir, userName + '.key.pem') - self.proxyPath = path or os.path.join(self.userDir, userName + '.pem') + self.proxyPath = os.path.join(self.userDir, userName + '.pem') if not vo: chain = X509Chain() # Load user cert and key @@ -147,9 +152,7 @@ def createProxy(self, userName, group, time, rfc=True, limit=False, vo=None, rol if 'bad decrypt' in retVal['Message']: return S_ERROR("Bad passphrase") return S_ERROR("Can't load %s" % userKeyFile) - result = chain.generateProxyToFile(self.proxyPath, time * 3600, - limited=limit, diracGroup=group, - rfc=rfc) + result = chain.generateProxyToFile(self.proxyPath, time * 3600, diracGroup=group) if not result['OK']: return result else: @@ -158,11 +161,7 @@ def createProxy(self, userName, group, time, rfc=True, limit=False, vo=None, rol cmd += ' -uri fakeserver.cern.ch:15000' cmd += ' -voms "%s%s"' % (vo, role and ':%s' % role or '') cmd += ' -fqan "/%s/Role=%s/Capability=NULL"' % (vo, role) - cmd += ' -hours %s -out %s' % (time, self.proxyPath) - if limit: - cmd += ' -limited' - if rfc: - cmd += ' -rfc' + cmd += ' -hours %s -out %s -rfc' % (time, self.proxyPath) status, output = commands.getstatusoutput(cmd) if status: return S_ERROR(output) @@ -281,7 +280,15 @@ def setUpClass(cls): exit() gLogger.debug(output) + # Result + status, output = commands.getstatusoutput('ls -al %s' % cls.userDir) + if status: + gLogger.error(output) + exit() + gLogger.debug('User certificates:\n', output) + def setUp(self): + gLogger.debug('\n') if self.failed: self.fail(self.failed) db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user_1", "user_2", "user_3")') @@ -319,10 +326,11 @@ def test_connectDB(self): self.assertTrue(res['OK']) def test_getUsers(self): - """ Try to get users from DB + """ Test 'getUsers' - try to get users from DB """ field = '("%%s", "/C=DN/O=DIRAC/CN=%%s", %%s "PEM", TIMESTAMPADD(SECOND, %%s, UTC_TIMESTAMP()))%s' % '' # Fill table for test + gLogger.info('\n* Fill tables for test..') for table, values, fields in [('ProxyDB_Proxies', [field % ('user_1', 'user_1', '"group_1",', '800'), field % ('user_2', 'user_2', '"group_1",', '-1')], @@ -331,32 +339,36 @@ def test_getUsers(self): [field % ('user_3', 'user_3', '', '43200')], '(UserName, UserDN, Pem, ExpirationTime)')]: result = db._update('INSERT INTO %s%s VALUES %s ;' % (table, fields, ', '.join(values))) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + # Testing 'getUsers' + gLogger.info('\n* Run `purgeExpiredProxies()`..') for user, exp, expect, log in [(False, 0, ['user_1', 'user_2', 'user_3'], '\n* Without arguments'), (False, 1200, ['user_3'], '* Request proxy live time'), ('user_2', 0, ['user_2'], '* Request user name'), ('no_user', 0, [], '* Request not exist user name')]: gLogger.info('%s..' % log) result = db.getUsers(validSecondsLeft=exp, userMask=user) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) usersList = [] for line in result['Value']: if line['Name'] in ['user_1', 'user_2', 'user_3']: usersList.append(line['Name']) - self.assertEqual(set(expect), set(usersList), '%s, when expected %s' % (usersList, expect)) + self.assertEqual(set(expect), set(usersList), str(usersList) + ', when expected ' + str(expect)) def test_purgeExpiredProxies(self): - """ Try to purge expired proxies + """ Test 'purgeExpiredProxies' - try to purge expired proxies """ + # Purge existed proxies + gLogger.info('\n* First cleaning..') cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, -1, UTC_TIMESTAMP()));' result = db._query(cmd) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) cmd = 'SELECT COUNT( * ) FROM ProxyDB_Proxies WHERE ExpirationTime < UTC_TIMESTAMP()' self.assertTrue(bool(db._query(cmd)['Value'][0][0] > 0)) result = db.purgeExpiredProxies() - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(result['Value'] > 0, 'Must be more then null') self.assertFalse(bool(db._query(cmd)['Value'][0][0] > 0), "Must be null") @@ -365,7 +377,7 @@ def test_getRemoveProxy(self): """ gLogger.info('\n* Check that DB is clean..') result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Check posible crashes when get proxy..') @@ -374,14 +386,14 @@ def test_getRemoveProxy(self): cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, 1800, UTC_TIMESTAMP()));' result = db._update(cmd) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Try to no correct getProxy requests for dn, group, reqtime, log in [('/C=DN/O=DIRAC/CN=user_1', 'group_1', 9999, 'No proxy provider, set request time, not valid proxy in ProxyDB_Proxies'), ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 0, 'Not valid proxy in ProxyDB_Proxies'), ('/C=DN/O=DIRAC/CN=no_user', 'no_valid_group', 0, - 'User no exist, proxy not in DB tables'), + 'User not exist, proxy not in DB tables'), ('/C=DN/O=DIRAC/CN=user_1', 'no_valid_group', 0, 'Group not valid, proxy not in DB tables'), ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 0, @@ -394,33 +406,33 @@ def test_getRemoveProxy(self): gLogger.info('Msg: %s' % result['Message']) # In the last case method found proxy and must to delete it as not valid cmd = 'SELECT COUNT( * ) FROM ProxyDB_Proxies WHERE UserName="user_1"' - self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 0), "GetProxy method didn't delete proxy.") + self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 0), "GetProxy method didn't delete the last proxy.") gLogger.info('* Check that DB is clean..') result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Generate proxy on the fly..') result = db.getProxy('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 1800) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) gLogger.info('* Check that ProxyDB_CleanProxy contain generated proxy..') result = db.getProxiesContent({'UserName': 'user_ca'}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 1), 'Generated proxy must be one.') for table, count in [('ProxyDB_Proxies', 0), ('ProxyDB_CleanProxies', 1)]: cmd = 'SELECT COUNT( * ) FROM %s WHERE UserName="user_ca"' % table self.assertTrue(bool(db._query(cmd)['Value'][0][0] == count), - '%s must %s' % (table, count and 'contain proxy' or 'be empty')) + table + ' must ' + (count and 'contain proxy' or 'be empty')) gLogger.info('* Check that DB is clean..') result = db.deleteProxy('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', proxyProvider='DIRAC_CA') - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Upload proxy..') @@ -434,33 +446,45 @@ def test_getRemoveProxy(self): False, 'Not exist user'), ("user_1", '/C=DN/O=DIRAC/CN=user_1', False, False, 12, True, 'Valid proxy')]: - result = db._update('DELETE FROM ProxyDB_Proxies WHERE UserName = "user_1"') - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + for table in ['ProxyDB_Proxies', 'ProxyDB_CleanProxies']: + result = db._update('DELETE FROM %s WHERE UserName = "user_1"' % table) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) gLogger.info('== > %s:' % log) result = self.createProxy(user, group, time, vo=vo) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + if not result['OK']: + result = ca.getFakeProxy(dn, time * 3600, vo=vo, group=group) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) chain = result['Value'][0] + + # Assert VOMSProxy + if vo: + self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') + result = db.generateDelegationRequest(chain, dn) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) resDict = result['Value'] result = chain.generateChainFromRequestString(resDict['request'], time * 3500) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + if not chain.isVOMS().get('Value') and vo: + gLogger.info('voms-proxy-fake command not working as expected, so proxy have no VOMS extention') + res = not res result = db.completeDelegation(resDict['id'], dn, result['Value']) - self.assertEqual(result['OK'], res, 'Must be ended %s%s' % - (res and 'successful' or 'with error', - ': %s' % result.get('Message') or 'Error message is absent.')) + text = 'Must be ended %s%s' % (res and 'successful' or 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) if not res: gLogger.info('Msg: %s' % (result['Message'])) cmd = 'SELECT COUNT( * ) FROM ProxyDB_Proxies WHERE UserName="%s"' % user self.assertTrue(bool(db._query(cmd)['Value'][0][0] == (res and group and 1) or 0), - 'ProxyDB_Proxies must %s' % (res and 'contain proxy' or 'be empty')) + 'ProxyDB_Proxies must ' + (res and 'contain proxy' or 'be empty')) cmd = 'SELECT COUNT( * ) FROM ProxyDB_CleanProxies WHERE UserName="%s"' % user self.assertTrue(bool(db._query(cmd)['Value'][0][0] == (res and not group and 1) or 0), - 'ProxyDB_CleanProxies must %s' % (res and 'contain proxy' or 'be empty')) + 'ProxyDB_CleanProxies must ' + (res and 'contain proxy' or 'be empty')) gLogger.info('* Check that ProxyDB_CleanProxy contain generated proxy..') result = db.getProxiesContent({'UserName': 'user_1'}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 1), 'Generated proxy must be one.') cmd = 'SELECT COUNT( * ) FROM ProxyDB_CleanProxies WHERE UserName="user_1"' self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 1), 'ProxyDB_CleanProxies must contain proxy') @@ -472,71 +496,81 @@ def test_getRemoveProxy(self): (True, 'group_1', 0, 'Request time less that in stored proxy')]: gLogger.info('== > %s:' % log) result = db.getProxy('/C=DN/O=DIRAC/CN=user_1', group, reqtime) - self.assertEqual(result['OK'], res, 'Must be ended %s%s' % - (res and 'successful' or 'with error', - ': %s' % result.get('Message') or 'Error message is absent.')) + text = 'Must be ended %s%s' % (res and 'successful' or 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) if res: chain = result['Value'][0] - self.assertTrue(chain.isValidProxy()['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(chain.isValidProxy()['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = chain.getDIRACGroup() - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - self.assertEqual('group_1', result['Value'], 'Group must be group_1, not %s' % result['Value']) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + self.assertEqual('group_1', result['Value'], 'Group must be group_1, not ' + result['Value']) else: gLogger.info('Msg: %s' % (result['Message'])) gLogger.info('* Check that DB is clean..') result = db.deleteProxy('/C=DN/O=DIRAC/CN=user_1', proxyProvider='Certificate') - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Get proxy when it store only in ProxyDB_Proxies..') # Make record with proxy that contain group - result = self.createProxy('user_1', group, 12) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + result = ca.getFakeProxy('/C=DN/O=DIRAC/CN=user_1', 12 * 3600, group='group_1') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) proxyStr = result['Value'][1] cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' cmd += '("user_1", "%s", "%s", "%s", TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' % (dn, group, proxyStr) result = db._update(cmd) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Try to get it result = db.getProxy(dn, group, 1800) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Check that proxy contain group chain = result['Value'][0] - self.assertTrue(chain.isValidProxy()['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(chain.isValidProxy()['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = chain.getDIRACGroup() - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - self.assertEqual('group_1', result['Value'], 'Group must be group_1, not %s' % result['Value']) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + self.assertEqual('group_1', result['Value'], 'Group must be group_1, not ' + result['Value']) gLogger.info('* Check that DB is clean..') result = db.deleteProxy('/C=DN/O=DIRAC/CN=user_1') - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Get VOMS proxy..') # Create proxy with VOMS extension result = self.createProxy('user_1', 'group_1', 12, vo='vo_1', role='role_2') - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + if not result['OK']: + result = ca.getFakeProxy('/C=DN/O=DIRAC/CN=user_1', 12 * 3600, group='group_1', vo='vo_1', role='role_2') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + chain, proxyStr = result['Value'] + + # Assert VOMSProxy + self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') - proxyStr = result['Value'][1] cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "%s", ' % proxyStr cmd += 'TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' result = db._update(cmd) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + vomsCase = ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, + 'Stored proxy already have different VOMS extension') + if not chain.isVOMS().get('Value'): + vomsCase = (False, False, False, False, False) # Try to get proxy with VOMS extension for dn, group, role, time, log in [('/C=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, - 'Not exist VO for current group'), - ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, - 'Stored proxy already have different VOMS extension'), + 'Not exist VO for current group'), vomsCase, ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 'role_1', 9999, 'Not correct VO configuration')]: gLogger.info('== > %s:' % log) + if not all([dn, group, role, time, log]): + gLogger.info('voms-proxy-fake command not working as expected, proxy have no VOMS extention, go to the next..') + continue result = db.getVOMSProxy(dn, group, time, role) self.assertFalse(result['OK'], 'Must be fail.') gLogger.info('Msg: %s' % result['Message']) @@ -550,7 +584,7 @@ def test_getRemoveProxy(self): ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'ProxyDB_CleanProxies')]: result = db.deleteProxy(dn) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) cmd = 'SELECT COUNT( * ) FROM %s WHERE UserName="user_ca"' % table self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 0)) From 0c84a28bc78f552f2fa7d248b6d14192329d470b Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 9 Mar 2020 18:47:28 +0100 Subject: [PATCH 02/52] fix CN in test proxy/cert --- tests/Integration/Framework/Test_ProxyDB.py | 114 ++++++++++---------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index 0098b501972..e228c56e8c5 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -69,12 +69,12 @@ } } } - user_1 + user { - DN = /C=DN/O=DIRAC/CN=user_1 + DN = /O=DN/O=DIRAC/CN=user DNProperties { - -C_DN-O_DIRAC-OU_user_1 + -O_DN-O_DIRAC-OU_user { ProxyProviders = Groups = dirac_user @@ -83,29 +83,29 @@ } user_2 { - DN = /C=DN/O=DIRAC/CN=user_2 + DN = /O=DN/O=DIRAC/CN=user_2 DNProperties { - -C_DN-O_DIRAC-OU_user_2 + -O_DN-O_DIRAC-OU_user_2 { } } } user_3 { - DN = /C=DN/O=DIRAC/CN=user_3 + DN = /O=DN/O=DIRAC/CN=user_3 } # Not in dirac_user group user_4 { - DN = /C=DN/O=DIRAC/CN=user_4 + DN = /O=DN/O=DIRAC/CN=user_4 } } Groups { group_1 { - Users = user_ca, user_1, user_2, user_3 + Users = user_ca, user, user_2, user_3 VO = vo_1 } group_2 @@ -159,7 +159,7 @@ def createProxy(self, userName, group, time, vo=None, role=None): cmd = 'voms-proxy-fake --cert %s --key %s -q' % (userCertFile, userKeyFile) cmd += ' -hostcert %s -hostkey %s' % (self.hostCert, self.hostKey) cmd += ' -uri fakeserver.cern.ch:15000' - cmd += ' -voms "%s%s"' % (vo, role and ':%s' % role or '') + cmd += ' -voms "%s"' % vo cmd += ' -fqan "/%s/Role=%s/Capability=NULL"' % (vo, role) cmd += ' -hours %s -out %s -rfc' % (time, self.proxyPath) status, output = commands.getstatusoutput(cmd) @@ -237,16 +237,16 @@ def setUpClass(cls): cls.userDir = tempfile.mkdtemp(dir=certsPath) # Create user certificates - for userName in ['no_user', 'user_1', 'user_2', 'user_3']: + for userName in ['no_user', 'user', 'user_2', 'user_3']: userConf = """[ req ] - default_bits = 2048 + default_bits = 4096 encrypt_key = yes distinguished_name = req_dn prompt = no req_extensions = v3_req [ req_dn ] - C = DN - O = DIRAC + O = DN + 0.O = DIRAC CN = %s [ v3_req ] # Extensions for client certificates (`man x509v3_config`). @@ -260,7 +260,7 @@ def setUpClass(cls): userCertFile = os.path.join(cls.userDir, userName + '.cert.pem') with open(userConfFile, "w") as f: f.write(userConf) - status, output = commands.getstatusoutput('openssl genrsa -out %s 2048' % userKeyFile) + status, output = commands.getstatusoutput('openssl genrsa -out %s' % userKeyFile) if status: gLogger.error(output) exit() @@ -291,12 +291,12 @@ def setUp(self): gLogger.debug('\n') if self.failed: self.fail(self.failed) - db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user_1", "user_2", "user_3")') - db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user_1", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') def tearDown(self): - db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user_1", "user_2", "user_3")') - db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user_1", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') @classmethod def tearDownClass(cls): @@ -328,11 +328,11 @@ def test_connectDB(self): def test_getUsers(self): """ Test 'getUsers' - try to get users from DB """ - field = '("%%s", "/C=DN/O=DIRAC/CN=%%s", %%s "PEM", TIMESTAMPADD(SECOND, %%s, UTC_TIMESTAMP()))%s' % '' + field = '("%%s", "/O=DN/O=DIRAC/CN=%%s", %%s "PEM", TIMESTAMPADD(SECOND, %%s, UTC_TIMESTAMP()))%s' % '' # Fill table for test gLogger.info('\n* Fill tables for test..') for table, values, fields in [('ProxyDB_Proxies', - [field % ('user_1', 'user_1', '"group_1",', '800'), + [field % ('user', 'user', '"group_1",', '800'), field % ('user_2', 'user_2', '"group_1",', '-1')], '(UserName, UserDN, UserGroup, Pem, ExpirationTime)'), ('ProxyDB_CleanProxies', @@ -342,7 +342,7 @@ def test_getUsers(self): self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Testing 'getUsers' gLogger.info('\n* Run `purgeExpiredProxies()`..') - for user, exp, expect, log in [(False, 0, ['user_1', 'user_2', 'user_3'], '\n* Without arguments'), + for user, exp, expect, log in [(False, 0, ['user', 'user_2', 'user_3'], '\n* Without arguments'), (False, 1200, ['user_3'], '* Request proxy live time'), ('user_2', 0, ['user_2'], '* Request user name'), ('no_user', 0, [], '* Request not exist user name')]: @@ -351,7 +351,7 @@ def test_getUsers(self): self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) usersList = [] for line in result['Value']: - if line['Name'] in ['user_1', 'user_2', 'user_3']: + if line['Name'] in ['user', 'user_2', 'user_3']: usersList.append(line['Name']) self.assertEqual(set(expect), set(usersList), str(usersList) + ', when expected ' + str(expect)) @@ -361,7 +361,7 @@ def test_purgeExpiredProxies(self): # Purge existed proxies gLogger.info('\n* First cleaning..') cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "PEM", ' + cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, -1, UTC_TIMESTAMP()));' result = db._query(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) @@ -376,40 +376,40 @@ def test_getRemoveProxy(self): """ Testing get, store proxy """ gLogger.info('\n* Check that DB is clean..') - result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Check posible crashes when get proxy..') # Make record with not valid proxy, valid group, user and short expired time cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "PEM", ' + cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, 1800, UTC_TIMESTAMP()));' result = db._update(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Try to no correct getProxy requests - for dn, group, reqtime, log in [('/C=DN/O=DIRAC/CN=user_1', 'group_1', 9999, + for dn, group, reqtime, log in [('/O=DN/O=DIRAC/CN=user', 'group_1', 9999, 'No proxy provider, set request time, not valid proxy in ProxyDB_Proxies'), - ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 0, + ('/O=DN/O=DIRAC/CN=user', 'group_1', 0, 'Not valid proxy in ProxyDB_Proxies'), - ('/C=DN/O=DIRAC/CN=no_user', 'no_valid_group', 0, + ('/O=DN/O=DIRAC/CN=no_user', 'no_valid_group', 0, 'User not exist, proxy not in DB tables'), - ('/C=DN/O=DIRAC/CN=user_1', 'no_valid_group', 0, + ('/O=DN/O=DIRAC/CN=user', 'no_valid_group', 0, 'Group not valid, proxy not in DB tables'), - ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 0, + ('/O=DN/O=DIRAC/CN=user', 'group_1', 0, 'No proxy provider for user, proxy not in DB tables'), - ('/C=DN/O=DIRAC/CN=user_4', 'group_2', 0, + ('/O=DN/O=DIRAC/CN=user_4', 'group_2', 0, 'Group has option enableToDownload = False in CS')]: gLogger.info('== > %s:' % log) result = db.getProxy(dn, group, reqtime) self.assertFalse(result['OK'], 'Must be fail.') gLogger.info('Msg: %s' % result['Message']) # In the last case method found proxy and must to delete it as not valid - cmd = 'SELECT COUNT( * ) FROM ProxyDB_Proxies WHERE UserName="user_1"' + cmd = 'SELECT COUNT( * ) FROM ProxyDB_Proxies WHERE UserName="user"' self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 0), "GetProxy method didn't delete the last proxy.") gLogger.info('* Check that DB is clean..') - result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') @@ -431,23 +431,23 @@ def test_getRemoveProxy(self): result = db.deleteProxy('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', proxyProvider='DIRAC_CA') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Upload proxy..') - for user, dn, group, vo, time, res, log in [("user_1", '/C=DN/O=DIRAC/CN=user_1', "group_1", False, 12, + for user, dn, group, vo, time, res, log in [("user", '/O=DN/O=DIRAC/CN=user', "group_1", False, 12, True, 'With group extension'), - ("user_1", '/C=DN/O=DIRAC/CN=user_1', False, "vo_1", 12, + ("user", '/O=DN/O=DIRAC/CN=user', False, "vo_1", 12, False, 'With voms extension'), - ("user_1", '/C=DN/O=DIRAC/CN=user_1', False, False, 0, + ("user", '/O=DN/O=DIRAC/CN=user', False, False, 0, False, 'Expired proxy'), - ("no_user", '/C=DN/O=DIRAC/CN=no_user', False, False, 12, + ("no_user", '/O=DN/O=DIRAC/CN=no_user', False, False, 12, False, 'Not exist user'), - ("user_1", '/C=DN/O=DIRAC/CN=user_1', False, False, 12, + ("user", '/O=DN/O=DIRAC/CN=user', False, False, 12, True, 'Valid proxy')]: for table in ['ProxyDB_Proxies', 'ProxyDB_CleanProxies']: - result = db._update('DELETE FROM %s WHERE UserName = "user_1"' % table) + result = db._update('DELETE FROM %s WHERE UserName = "user"' % table) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) gLogger.info('== > %s:' % log) @@ -483,10 +483,10 @@ def test_getRemoveProxy(self): 'ProxyDB_CleanProxies must ' + (res and 'contain proxy' or 'be empty')) gLogger.info('* Check that ProxyDB_CleanProxy contain generated proxy..') - result = db.getProxiesContent({'UserName': 'user_1'}, {}) + result = db.getProxiesContent({'UserName': 'user'}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 1), 'Generated proxy must be one.') - cmd = 'SELECT COUNT( * ) FROM ProxyDB_CleanProxies WHERE UserName="user_1"' + cmd = 'SELECT COUNT( * ) FROM ProxyDB_CleanProxies WHERE UserName="user"' self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 1), 'ProxyDB_CleanProxies must contain proxy') gLogger.info('* Get proxy that store only in ProxyDB_CleanProxies..') @@ -495,7 +495,7 @@ def test_getRemoveProxy(self): (False, 'group_2', 0, 'Request group not contain user'), (True, 'group_1', 0, 'Request time less that in stored proxy')]: gLogger.info('== > %s:' % log) - result = db.getProxy('/C=DN/O=DIRAC/CN=user_1', group, reqtime) + result = db.getProxy('/O=DN/O=DIRAC/CN=user', group, reqtime) text = 'Must be ended %s%s' % (res and 'successful' or 'with error', ': %s' % result.get('Message', 'Error message is absent.')) self.assertEqual(result['OK'], res, text) @@ -509,19 +509,19 @@ def test_getRemoveProxy(self): gLogger.info('Msg: %s' % (result['Message'])) gLogger.info('* Check that DB is clean..') - result = db.deleteProxy('/C=DN/O=DIRAC/CN=user_1', proxyProvider='Certificate') + result = db.deleteProxy('/O=DN/O=DIRAC/CN=user', proxyProvider='Certificate') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Get proxy when it store only in ProxyDB_Proxies..') # Make record with proxy that contain group - result = ca.getFakeProxy('/C=DN/O=DIRAC/CN=user_1', 12 * 3600, group='group_1') + result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) proxyStr = result['Value'][1] cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user_1", "%s", "%s", "%s", TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' % (dn, group, + cmd += '("user", "%s", "%s", "%s", TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' % (dn, group, proxyStr) result = db._update(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) @@ -536,17 +536,17 @@ def test_getRemoveProxy(self): self.assertEqual('group_1', result['Value'], 'Group must be group_1, not ' + result['Value']) gLogger.info('* Check that DB is clean..') - result = db.deleteProxy('/C=DN/O=DIRAC/CN=user_1') + result = db.deleteProxy('/O=DN/O=DIRAC/CN=user') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - result = db.getProxiesContent({'UserName': ['user_ca', 'user_1', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Get VOMS proxy..') # Create proxy with VOMS extension - result = self.createProxy('user_1', 'group_1', 12, vo='vo_1', role='role_2') + result = self.createProxy('user', 'group_1', 12, vo='vo_1', role='role_2') if not result['OK']: - result = ca.getFakeProxy('/C=DN/O=DIRAC/CN=user_1', 12 * 3600, group='group_1', vo='vo_1', role='role_2') + result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1', vo='vo_1', role='role_2') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) chain, proxyStr = result['Value'] @@ -554,33 +554,33 @@ def test_getRemoveProxy(self): self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user_1", "/C=DN/O=DIRAC/CN=user_1", "group_1", "%s", ' % proxyStr + cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "%s", ' % proxyStr cmd += 'TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' result = db._update(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - vomsCase = ('/C=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, + vomsCase = ('/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, 'Stored proxy already have different VOMS extension') if not chain.isVOMS().get('Value'): vomsCase = (False, False, False, False, False) # Try to get proxy with VOMS extension - for dn, group, role, time, log in [('/C=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, + for dn, group, role, time, log in [('/O=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, 'Not exist VO for current group'), vomsCase, ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 'role_1', 9999, 'Not correct VO configuration')]: gLogger.info('== > %s:' % log) - if not all([dn, group, role, time, log]): + if not any([dn, group, role, time, log]): gLogger.info('voms-proxy-fake command not working as expected, proxy have no VOMS extention, go to the next..') continue result = db.getVOMSProxy(dn, group, time, role) self.assertFalse(result['OK'], 'Must be fail.') gLogger.info('Msg: %s' % result['Message']) # Check stored proxies - for table, user, count in [('ProxyDB_Proxies', 'user_1', 1), ('ProxyDB_CleanProxies', 'user_ca', 1)]: + for table, user, count in [('ProxyDB_Proxies', 'user', 1), ('ProxyDB_CleanProxies', 'user_ca', 1)]: cmd = 'SELECT COUNT( * ) FROM %s WHERE UserName="%s"' % (table, user) self.assertTrue(bool(db._query(cmd)['Value'][0][0] == count)) gLogger.info('* Delete proxies..') - for dn, table in [('/C=DN/O=DIRAC/CN=user_1', 'ProxyDB_Proxies'), + for dn, table in [('/O=DN/O=DIRAC/CN=user', 'ProxyDB_Proxies'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'ProxyDB_CleanProxies')]: result = db.deleteProxy(dn) From 69a6b8c33d24b6f758265c3195d4fa1a8c67839c Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 9 Mar 2020 19:37:05 +0100 Subject: [PATCH 03/52] pep8 --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index d354d2a9ddf..8ab0c071554 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -314,9 +314,9 @@ def __fillX509Name(self, field, value): def __createCertM2Crypto(self, extensions=None): """ Create new certificate for user - + :param list extensions: list of X509.new_extension() - + :return: S_OK(tuple)/S_ERROR() -- tuple contain certificate and pulic key as strings """ # Create publik key @@ -360,6 +360,7 @@ def __createCertM2Crypto(self, extensions=None): def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): """ Get fake proxy for tests + :param str dn: fake DN :param int time: expired time in a seconds :param str vo: fake VOMS VO name @@ -367,6 +368,7 @@ def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): :param dict kwargs: fake VO description dictionary with possible fields: - Expired time - Role, etc.. + :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string """ self.__X509Name = X509.X509_Name() @@ -385,4 +387,4 @@ def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): return result chain = X509Chain() chain.loadProxyFromString(result['Value']) - return S_OK((chain, result['Value'])) \ No newline at end of file + return S_OK((chain, result['Value'])) From 108e695237433ec683631911c490617491891422 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Tue, 10 Mar 2020 14:09:41 +0100 Subject: [PATCH 04/52] self.caDict to caDict --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 8ab0c071554..7bde2558fc7 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -51,7 +51,7 @@ def __init__(self, parameters=None): if nid not in self.n2field: self.n2field[nid] = field self.n2field[nid] = len(field) < len(self.n2field[nid]) and field or self.n2field[nid] - self.caDict = {} + caDict = {} self.caFieldByNid = {} def setParameters(self, parameters): @@ -184,8 +184,8 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): if not result['OK']: return result caDN = result['Value']['subject'] - self.caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) - self.caFieldByNid = dict([[self.fs2nid[field], field] for field in self.caDict]) + caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) + self.caFieldByNid = dict([[self.fs2nid[field], field] for field in caDict]) dnDict = {} if userDN: @@ -198,9 +198,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): for nid in dnFieldByNid: if nid not in self.supplied + self.match + self.optional: return S_ERROR('Current DN is invalid, "%s" field is not found for current CA.' % dnFieldByNid[nid]) - if nid in self.match and not self.caDict[self.caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: + if nid in self.match and not caDict[self.caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: return S_ERROR('Current DN is invalid, "%s" field must be %s.' % (dnFieldByNid[nid], - self.caDict[self.caFieldByNid[nid]])) + caDict[self.caFieldByNid[nid]])) if nid in self.maxDict and len(dnDict[dnFieldByNid[nid]]) > self.maxDict[nid]: return S_ERROR('Current DN is invalid, "%s" field must be less then %s.' % (dnDict[dnFieldByNid[nid]], self.maxDict[nid])) @@ -230,7 +230,7 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): for nid in self.match: if nid not in self.caFieldByNid: return S_ERROR('Distributes name(%s) must be present in CA certificate.' % ', '.join(self.n2fields[nid])) - result = self.__fillX509Name(self.caFieldByNid[nid], self.caDict[self.caFieldByNid[nid]]) + result = self.__fillX509Name(self.caFieldByNid[nid], caDict[self.caFieldByNid[nid]]) if not result['OK']: return result # Test supplied fields From aff07e05143f5e3349ac431a8f1d5452ca661c02 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Tue, 10 Mar 2020 14:40:08 +0100 Subject: [PATCH 05/52] return dn whith _ --- .../ProxyProvider/DIRACCAProxyProvider.py | 1 - tests/Integration/Framework/Test_ProxyDB.py | 72 ++++++++++++------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 7bde2558fc7..a71218ce204 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -51,7 +51,6 @@ def __init__(self, parameters=None): if nid not in self.n2field: self.n2field[nid] = field self.n2field[nid] = len(field) < len(self.n2field[nid]) and field or self.n2field[nid] - caDict = {} self.caFieldByNid = {} def setParameters(self, parameters): diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index e228c56e8c5..a96c4f6aebb 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -81,6 +81,18 @@ } } } + user_1 + { + DN = /O=DN/O=DIRAC/CN=user_1 + DNProperties + { + -O_DN-O_DIRAC-OU_user_1 + { + ProxyProviders = + Groups = dirac_user + } + } + } user_2 { DN = /O=DN/O=DIRAC/CN=user_2 @@ -105,7 +117,7 @@ { group_1 { - Users = user_ca, user, user_2, user_3 + Users = user_ca, user, user_1, user_2, user_3 VO = vo_1 } group_2 @@ -237,7 +249,7 @@ def setUpClass(cls): cls.userDir = tempfile.mkdtemp(dir=certsPath) # Create user certificates - for userName in ['no_user', 'user', 'user_2', 'user_3']: + for userName in ['no_user', 'user', 'user_1', 'user_2', 'user_3']: userConf = """[ req ] default_bits = 4096 encrypt_key = yes @@ -291,12 +303,12 @@ def setUp(self): gLogger.debug('\n') if self.failed: self.fail(self.failed) - db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') - db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_1", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_1", "user_2", "user_3")') def tearDown(self): - db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') - db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_Proxies WHERE UserName IN ("user_ca", "user", "user_1", "user_2", "user_3")') + db._update('DELETE FROM ProxyDB_CleanProxies WHERE UserName IN ("user_ca", "user", "user_1", "user_2", "user_3")') @classmethod def tearDownClass(cls): @@ -376,7 +388,7 @@ def test_getRemoveProxy(self): """ Testing get, store proxy """ gLogger.info('\n* Check that DB is clean..') - result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_1' 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') @@ -409,7 +421,7 @@ def test_getRemoveProxy(self): self.assertTrue(bool(db._query(cmd)['Value'][0][0] == 0), "GetProxy method didn't delete the last proxy.") gLogger.info('* Check that DB is clean..') - result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_1', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') @@ -431,7 +443,7 @@ def test_getRemoveProxy(self): result = db.deleteProxy('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', proxyProvider='DIRAC_CA') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_1', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') @@ -440,6 +452,8 @@ def test_getRemoveProxy(self): True, 'With group extension'), ("user", '/O=DN/O=DIRAC/CN=user', False, "vo_1", 12, False, 'With voms extension'), + ("user_1", '/O=DN/O=DIRAC/CN=user_1', False, "vo_1", 12, + False, 'With voms extension'), ("user", '/O=DN/O=DIRAC/CN=user', False, False, 0, False, 'Expired proxy'), ("no_user", '/O=DN/O=DIRAC/CN=no_user', False, False, 12, @@ -449,6 +463,8 @@ def test_getRemoveProxy(self): for table in ['ProxyDB_Proxies', 'ProxyDB_CleanProxies']: result = db._update('DELETE FROM %s WHERE UserName = "user"' % table) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + result = db._update('DELETE FROM %s WHERE UserName = "user_1"' % table) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) gLogger.info('== > %s:' % log) result = self.createProxy(user, group, time, vo=vo) @@ -543,28 +559,30 @@ def test_getRemoveProxy(self): self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Get VOMS proxy..') - # Create proxy with VOMS extension - result = self.createProxy('user', 'group_1', 12, vo='vo_1', role='role_2') - if not result['OK']: - result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1', vo='vo_1', role='role_2') - self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - chain, proxyStr = result['Value'] + for vomsuser in ['user', 'user_1']: + # Create proxy with VOMS extension + result = self.createProxy(vomsuser, 'group_1', 12, vo='vo_1', role='role_2') + if not result['OK']: + result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=%s' % vomsuser, 12 * 3600, group='group_1', vo='vo_1', role='role_2') + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + chain, proxyStr = result['Value'] - # Assert VOMSProxy - self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') + # Assert VOMSProxy + self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') + + cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' + cmd += '("%s", "/O=DN/O=DIRAC/CN=%s", "group_1", "%s", ' % (vomsuser, vomsuser, proxyStr) + cmd += 'TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' + result = db._update(cmd) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "%s", ' % proxyStr - cmd += 'TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' - result = db._update(cmd) - self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - vomsCase = ('/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, - 'Stored proxy already have different VOMS extension') - if not chain.isVOMS().get('Value'): - vomsCase = (False, False, False, False, False) # Try to get proxy with VOMS extension for dn, group, role, time, log in [('/O=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, - 'Not exist VO for current group'), vomsCase, + 'Not exist VO for current group'), + ('/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, + 'Stored proxy already have different VOMS extension'), + ('/O=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, + 'Stored proxy already have different VOMS extension'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 'role_1', 9999, 'Not correct VO configuration')]: gLogger.info('== > %s:' % log) From 637d479cc69523c6418713e9c60a5e6479d922c6 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Tue, 10 Mar 2020 14:40:42 +0100 Subject: [PATCH 06/52] return dn whith _ --- tests/Integration/Framework/Test_ProxyDB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index a96c4f6aebb..2a8c439c4b5 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -554,7 +554,7 @@ def test_getRemoveProxy(self): gLogger.info('* Check that DB is clean..') result = db.deleteProxy('/O=DN/O=DIRAC/CN=user') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) + result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_1', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') From a076e9cfc4ec638fa814cbce158ee6d25a65a05f Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Tue, 10 Mar 2020 15:12:13 +0100 Subject: [PATCH 07/52] whitespace --- tests/Integration/Framework/Test_ProxyDB.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index 2a8c439c4b5..87db150bb3b 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -578,9 +578,9 @@ def test_getRemoveProxy(self): # Try to get proxy with VOMS extension for dn, group, role, time, log in [('/O=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, - 'Not exist VO for current group'), + 'Not exist VO for current group'), ('/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, - 'Stored proxy already have different VOMS extension'), + 'Stored proxy already have different VOMS extension'), ('/O=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, 'Stored proxy already have different VOMS extension'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', From 18cc85a5da56acdb1f70e69623b916eea23a1c98 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Wed, 11 Mar 2020 11:40:19 +0100 Subject: [PATCH 08/52] add log --- tests/Integration/Framework/Test_ProxyDB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index 87db150bb3b..63d55c7d166 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -585,7 +585,7 @@ def test_getRemoveProxy(self): 'Stored proxy already have different VOMS extension'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 'role_1', 9999, 'Not correct VO configuration')]: - gLogger.info('== > %s:' % log) + gLogger.info('== > %s(DN: %s):' % (log, dn)) if not any([dn, group, role, time, log]): gLogger.info('voms-proxy-fake command not working as expected, proxy have no VOMS extention, go to the next..') continue From b45e7bbf6f4b2777c4e0843e85062958ddb189a6 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 12 Mar 2020 13:17:58 +0100 Subject: [PATCH 09/52] Fix setPersistentFlag MySQL error --- FrameworkSystem/DB/ProxyDB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrameworkSystem/DB/ProxyDB.py b/FrameworkSystem/DB/ProxyDB.py index 346049df20c..d9ddaa8096d 100755 --- a/FrameworkSystem/DB/ProxyDB.py +++ b/FrameworkSystem/DB/ProxyDB.py @@ -1066,7 +1066,7 @@ def setPersistencyFlag(self, userDN, userGroup, persistent=True): return result userName = result['Value'] cmd = "INSERT INTO `ProxyDB_Proxies` ( UserName, UserDN, UserGroup, Pem, ExpirationTime, PersistentFlag ) VALUES " - cmd += "( %s, %s, %s, '', UTC_TIMESTAMP(), 'True' )" % (userName, sUserDN, sUserGroup) + cmd += "( '%s', %s, %s, '', UTC_TIMESTAMP(), 'True' )" % (userName, sUserDN, sUserGroup) else: cmd = "UPDATE `ProxyDB_Proxies` SET PersistentFlag='%s' WHERE UserDN=%s AND UserGroup=%s" % (sqlFlag, sUserDN, From 0e60fb85758f92287b9b197d9982b9f01a5ddeaa Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 13 Mar 2020 11:12:34 +0100 Subject: [PATCH 10/52] remove self.caFieldByNid --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index a71218ce204..878e12a5506 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -51,7 +51,6 @@ def __init__(self, parameters=None): if nid not in self.n2field: self.n2field[nid] = field self.n2field[nid] = len(field) < len(self.n2field[nid]) and field or self.n2field[nid] - self.caFieldByNid = {} def setParameters(self, parameters): """ Set new parameters @@ -184,7 +183,7 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): return result caDN = result['Value']['subject'] caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) - self.caFieldByNid = dict([[self.fs2nid[field], field] for field in caDict]) + caFieldByNid = dict([[self.fs2nid[field], field] for field in caDict]) dnDict = {} if userDN: @@ -197,9 +196,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): for nid in dnFieldByNid: if nid not in self.supplied + self.match + self.optional: return S_ERROR('Current DN is invalid, "%s" field is not found for current CA.' % dnFieldByNid[nid]) - if nid in self.match and not caDict[self.caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: + if nid in self.match and not caDict[caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: return S_ERROR('Current DN is invalid, "%s" field must be %s.' % (dnFieldByNid[nid], - caDict[self.caFieldByNid[nid]])) + caDict[caFieldByNid[nid]])) if nid in self.maxDict and len(dnDict[dnFieldByNid[nid]]) > self.maxDict[nid]: return S_ERROR('Current DN is invalid, "%s" field must be less then %s.' % (dnDict[dnFieldByNid[nid]], self.maxDict[nid])) @@ -227,9 +226,9 @@ def getUserDN(self, userDict=None, sessionDict=None, userDN=None): self.log.info('Creating distributes names chain') # Test match fields for nid in self.match: - if nid not in self.caFieldByNid: + if nid not in caFieldByNid: return S_ERROR('Distributes name(%s) must be present in CA certificate.' % ', '.join(self.n2fields[nid])) - result = self.__fillX509Name(self.caFieldByNid[nid], caDict[self.caFieldByNid[nid]]) + result = self.__fillX509Name(caFieldByNid[nid], caDict[caFieldByNid[nid]]) if not result['OK']: return result # Test supplied fields From 755f69637128827db99339882227db7c0a22aa57 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 13 Mar 2020 11:50:37 +0100 Subject: [PATCH 11/52] use _escapeString for username --- FrameworkSystem/DB/ProxyDB.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/FrameworkSystem/DB/ProxyDB.py b/FrameworkSystem/DB/ProxyDB.py index d9ddaa8096d..55e0fb2e1a5 100755 --- a/FrameworkSystem/DB/ProxyDB.py +++ b/FrameworkSystem/DB/ProxyDB.py @@ -1064,9 +1064,12 @@ def setPersistencyFlag(self, userDN, userGroup, persistent=True): if not result['OK']: self.log.error("setPersistencyFlag: Can not retrieve username for DN", userDN) return result - userName = result['Value'] + try: + sUserName = self._escapeString(result['Value'])['Value'] + except KeyError: + return S_ERROR("Can't escape user name") cmd = "INSERT INTO `ProxyDB_Proxies` ( UserName, UserDN, UserGroup, Pem, ExpirationTime, PersistentFlag ) VALUES " - cmd += "( '%s', %s, %s, '', UTC_TIMESTAMP(), 'True' )" % (userName, sUserDN, sUserGroup) + cmd += "( %s, %s, %s, '', UTC_TIMESTAMP(), 'True' )" % (sUserName, sUserDN, sUserGroup) else: cmd = "UPDATE `ProxyDB_Proxies` SET PersistentFlag='%s' WHERE UserDN=%s AND UserGroup=%s" % (sqlFlag, sUserDN, From 88b39ec71f1fdee58bd81cb7bb44870368112429 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 00:03:48 +0100 Subject: [PATCH 12/52] add tests for DIRACCAProxyProvider --- FrameworkSystem/DB/ProxyDB.py | 2 +- .../ProxyProvider/DIRACCAProxyProvider.py | 359 +++++++++--------- .../test/Test_DIRACCAProxyProvider.py | 196 ++++++++++ Resources/ProxyProvider/test/__init__.py | 0 4 files changed, 387 insertions(+), 170 deletions(-) create mode 100644 Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py create mode 100644 Resources/ProxyProvider/test/__init__.py diff --git a/FrameworkSystem/DB/ProxyDB.py b/FrameworkSystem/DB/ProxyDB.py index 55e0fb2e1a5..122fc26889a 100755 --- a/FrameworkSystem/DB/ProxyDB.py +++ b/FrameworkSystem/DB/ProxyDB.py @@ -714,7 +714,7 @@ def __generateProxyFromProxyProvider(self, userDN, proxyProvider): if not result['OK']: return result pp = result['Value'] - result = pp.getProxy({"DN": userDN}) + result = pp.getProxy(userDN) if not result['OK']: return result proxyStr = result['Value']['proxy'] diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 878e12a5506..54f29866b52 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -11,8 +11,6 @@ from DIRAC import gLogger, S_OK, S_ERROR from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error -from DIRAC.Core.Security.X509Certificate import X509Certificate # pylint: disable=import-error -from DIRAC.ConfigurationSystem.Client.Helpers import Registry from DIRAC.Resources.ProxyProvider.ProxyProvider import ProxyProvider __RCSID__ = "$Id$" @@ -33,24 +31,26 @@ def __init__(self, parameters=None): self.match = [] self.supplied = ['CN'] self.optional = ['C', 'O', 'OU', 'emailAddress'] + self.dnList = ['C', 'O', 'OU', 'CN', 'emailAddress'] # Add not supported distributes names - self.fs2nid = X509.X509_Name.nid.copy() - self.fs2nid['DC'] = -1 - self.fs2nid['domainComponent'] = -1 - self.fs2nid['organizationalUnitName'] = 18 - self.fs2nid['countryName'] = 14 - self.n2field = {} # nid: most short or specidied in CS distributes name - self.n2fields = {} # nid: list of distributes names + self.fields2nid = X509.X509_Name.nid.copy() + self.fields2nid['DC'] = -1 + self.fields2nid['domainComponent'] = -1 + self.fields2nid['organizationalUnitName'] = 18 + self.fields2nid['countryName'] = 14 + self.nid2field = {} # nid: most short or specidied in CS distributes name + self.nid2fields = {} # nid: list of distributes names # Specify standart fields - for field in self.fs2nid: - if self.fs2nid[field] not in self.n2fields: - self.n2fields[self.fs2nid[field]] = [] - self.n2fields[self.fs2nid[field]].append(field) - for nid in self.n2fields: - for field in self.n2fields[nid]: - if nid not in self.n2field: - self.n2field[nid] = field - self.n2field[nid] = len(field) < len(self.n2field[nid]) and field or self.n2field[nid] + for field in self.fields2nid: + if self.fields2nid[field] not in self.nid2fields: + self.nid2fields[self.fields2nid[field]] = [] + self.nid2fields[self.fields2nid[field]].append(field) + for nid in self.nid2fields: + for field in self.nid2fields[nid]: + if nid not in self.nid2field: + self.nid2field[nid] = field + self.nid2field[nid] = len(field) < len(self.nid2field[nid]) and field or self.nid2field[nid] + self.dnInfoDictCA = {} def setParameters(self, parameters): """ Set new parameters @@ -62,7 +62,6 @@ def setParameters(self, parameters): # If CA configuration file exist self.parameters = parameters if parameters.get('CAConfigFile'): - self.supplied, self.optional = [], [] self.__parseCACFG() if 'Bits' in parameters: self.bits = int(parameters['Bits']) @@ -71,184 +70,188 @@ def setParameters(self, parameters): if 'Match' in parameters: self.match = [] for field in parameters['Match'].replace(' ', '').split(','): - self.match.append(self.fs2nid[field]) + self.match.append(self.fields2nid[field]) if 'Supplied' in parameters: self.supplied = [] for field in parameters['Supplied'].replace(' ', '').split(','): - self.supplied.append(self.fs2nid[field]) + self.supplied.append(self.fields2nid[field]) if 'Optional' in parameters: self.optional = [] for field in parameters['Optional'].replace(' ', '').split(','): - self.optional.append(self.fs2nid[field]) + self.optional.append(self.fields2nid[field]) + if 'DNOrder' in parameters: + if isinstance(parameters['DNOrder'], list): + self.dnList = parameters['DNOrder'] + else: + self.dnList = parameters['DNOrder'].replace(', ', ',').split(',') # Set defaults for distridutes names + for field, value in self.parameters.items(): + if field not in self.fields2nid: + continue + if self.fields2nid[field] not in self.optional + self.supplied + self.match: + del self.parameters[field] + elif not isinstance(self.parameters[field], list): + self.parameters[field] = self.parameters[field].replace(', ', ',').split(',') + self.defDict = {} for field, value in parameters.items(): - if field in self.fs2nid: + if field in self.fields2nid: self.defDict[field] = value - self.defFieldByNid = dict([[self.fs2nid[field], field] for field in self.defDict]) - for nid in self.n2field: + self.defFieldByNid = dict([[self.fields2nid[field], field] for field in self.defDict]) + for nid in self.nid2field: if nid in self.defFieldByNid: - self.n2field[nid] = self.defFieldByNid[nid] + self.nid2field[nid] = self.defFieldByNid[nid] self.match.sort() self.supplied.sort() - def checkStatus(self, userDict=None, sessionDict=None): + # Read CA certificate + chain = X509Chain() + result = chain.loadChainFromFile(self.parameters['CertFile']) + if result['OK']: + result = chain.getCredentials() + if result['OK']: + result = self.__parseDN(result['Value']['subject']) + if not result['OK']: + return result + self.dnInfoDictCA = result['Value'] + return S_OK() + + def checkStatus(self, userDN): """ Read ready to work status of proxy provider - :param dict userDict: user description dictionary with possible fields: - FullName, UserName, DN, Email, DiracGroup - :param dict sessionDict: session dictionary + :param str userDN: user DN :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: - 'Status' with ready to work status[ready, needToAuth] """ - self.log.info('Ckecking work status of', self.parameters['ProviderName']) - # Evaluate full name and e-mail of the user - fullName = userDict.get('FullName') - eMail = userDict.get('Email') - - reqDNs = userDict.get('DN') or [] - if not isinstance(reqDNs, list): - reqDNs = reqDNs.split(', ') - for reqDN in reqDNs: - # Get the DN info as a dictionary - result = Registry.getProxyProvidersForDN(reqDN) - if not result['OK']: - return result - if self.parameters['ProviderName'] not in result['Value']: - continue - dnDict = dict([field.split('=') for field in reqDN.lstrip('/').split('/')]) - if not fullName: - fullName = dnDict.get('CN') - if not eMail: - eMail = dnDict.get('emailAddress') - self.log.info('Full name:', fullName) - self.log.info('Email:', eMail) - if not fullName: - return S_ERROR("Incomplete user information: no full name found") - if not eMail: - return S_ERROR("Incomplete user information: no email found") + self.log.debug('Ckecking work status of', self.parameters['ProviderName']) + result = self.__parseDN(userDN) + if not result['OK']: + return result + dnInfoDict = result['Value'] + + try: + userNIDs = [self.fields2nid[f.split('=')[0]] for f in userDN.lstrip('/').split('/')] + except ValueError as e: + return S_ERROR('Unknown DN field in used DN: %s' % e) + nidOrder = [self.fields2nid[f] for f in self.dnList] + for index, nid in enumerate(userNIDs): + if nid not in nidOrder: + return S_ERROR('"%s" field not found in order.' % self.nid2field[nid]) + if index > nidOrder.index(nid): + return S_ERROR('Bad DNs order') + for i in range(nidOrder.index(nid) - 1): + try: + if userNIDs.index(nidOrder[i]) > index: + return S_ERROR('Bad DNs order') + except ValueError: + continue + for i in range(nidOrder.index(nid) + 1, len(nidOrder)): + try: + if userNIDs.index(nidOrder[i]) < index: + return S_ERROR('Bad DNs order') + except ValueError: + continue + + for nid in self.supplied: + if nid not in [self.fields2nid[f] for f in dnInfoDict]: + return S_ERROR('Current DN is invalid, "%s" field must be set.' % self.nid2field[nid]) + + for field, values in dnInfoDict.items(): + nid = self.fields2nid[field] + err = 'Current DN is invalid, "%s" field' % field + if nid not in self.supplied + self.match + self.optional: + return S_ERROR('%s is not found for current CA.' % err) + if nid in self.match and not self.dnInfoDictCA[field] == values: + return S_ERROR('%s must be /%s=%s.' % (err, field, + ('/%s=' % field).joing(self.dnInfoDictCA[field]))) + if nid in self.maxDict: + rangeMax = range(min(len(values), len(self.maxDict[nid]))) + if any([True if len(values[i]) > self.maxDict[nid][i] else False for i in rangeMax]): + return S_ERROR('%s values must be less then %s.' % (err, ', '.join(self.maxDict[nid]))) + if nid in self.minDict: + rangeMin = range(min(len(values), len(self.minDict[nid]))) + if any([True if len(values[i]) < self.minDict[nid][i] else False for i in rangeMin]): + return S_ERROR('%s values must be more then %s.' % (err, ', '.join(self.minDict[nid]))) return S_OK({'Status': 'ready'}) - def getProxy(self, userDict=None, sessionDict=None): + def getProxy(self, userDN): """ Generate user proxy - :param dict userDict: user description dictionary with possible fields: - FullName, UserName, DN, Email, DiracGroup - :param dict sessionDict: session dictionary + :param str userDN: user DN :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a proxy string """ - result = self.getUserDN(userDict, sessionDict, userDN=userDict.get('DN')) - if not result['OK']: - return result - - result = self.__createCertM2Crypto() - if not result['OK']: - return result - certStr, keyStr = result['Value'] + result = self.checkStatus(userDN) + if result['OK']: + result = self.__createCertM2Crypto() + if result['OK']: + certStr, keyStr = result['Value'] - chain = X509Chain() - result = chain.loadChainFromString(certStr) - if not result['OK']: - return result - result = chain.loadKeyFromString(keyStr) - if not result['OK']: - return result + chain = X509Chain() + result = chain.loadChainFromString(certStr) + if result['OK']: + result = chain.loadKeyFromString(keyStr) + if result['OK']: + result = chain.generateProxyToString(365 * 24 * 3600, rfc=True) - result = chain.generateProxyToString(365 * 24 * 3600, rfc=True) if not result['OK']: return result return S_OK({'proxy': result['Value']}) - def getUserDN(self, userDict=None, sessionDict=None, userDN=None): + def generateDN(self, **kwargs): """ Get DN of the user certificate that will be created - :param dict userDict: user description dictionary with possible fields: - FullName, UserName, DN, Email, DiracGroup - :param dict sessionDict: session dictionary - :param basestring userDN: user DN + :param dict kwargs: user description dictionary with possible fields: + - FullName or CN + - Email or emailAddress - :return: S_OK()/S_ERROR(), Value is the DN string + :return: S_OK(str)/S_ERROR() -- contain DN """ - userDict = userDict or {} - chain = X509Chain() - result = chain.loadChainFromFile(self.parameters['CertFile']) - if not result['OK']: - return result - result = chain.getCredentials() - if not result['OK']: - return result - caDN = result['Value']['subject'] - caDict = dict([field.split('=') for field in caDN.lstrip('/').split('/')]) - caFieldByNid = dict([[self.fs2nid[field], field] for field in caDict]) - - dnDict = {} - if userDN: - self.log.info('Checking %s user DN' % userDN) - dnDict = dict([field.split('=') for field in userDN.lstrip('/').split('/')]) - dnFieldByNid = dict([[self.fs2nid[field], field] for field in dnDict]) - for nid in self.supplied: - if nid not in dnFieldByNid: - return S_ERROR('Current DN is invalid, "%s" field must be set.' % self.n2field[nid]) - for nid in dnFieldByNid: - if nid not in self.supplied + self.match + self.optional: - return S_ERROR('Current DN is invalid, "%s" field is not found for current CA.' % dnFieldByNid[nid]) - if nid in self.match and not caDict[caFieldByNid[nid]] == dnDict[dnFieldByNid[nid]]: - return S_ERROR('Current DN is invalid, "%s" field must be %s.' % (dnFieldByNid[nid], - caDict[caFieldByNid[nid]])) - if nid in self.maxDict and len(dnDict[dnFieldByNid[nid]]) > self.maxDict[nid]: - return S_ERROR('Current DN is invalid, "%s" field must be less then %s.' % (dnDict[dnFieldByNid[nid]], - self.maxDict[nid])) - if nid in self.minDict and len(dnDict[dnFieldByNid[nid]]) < self.minDict[nid]: - return S_ERROR('Current DN is invalid, "%s" field must be more then %s.' % (dnDict[dnFieldByNid[nid]], - self.minDict[nid])) - for k, v in dnDict.items(): - if self.defDict.get(k): - self.defDict[k] = v - if self.fs2nid[k] == self.fs2nid['CN']: - userDict['FullName'] = v - if self.fs2nid[k] == self.fs2nid['emailAddress']: - userDict['Email'] = v - else: - result = self.checkStatus(userDict) - if not result['OK']: - return result + if kwargs.get('FullName'): + kwargs['CN'] = [kwargs['FullName']] + if kwargs.get('Email'): + kwargs['emailAddress'] = [kwargs['Email']] - # Fill DN subject name - if not userDict.get('FullName'): - return S_ERROR("Incomplete user information: no full name found") - if not userDict.get('Email'): - return S_ERROR("Incomplete user information: no email found") self.__X509Name = X509.X509_Name() self.log.info('Creating distributes names chain') - # Test match fields - for nid in self.match: - if nid not in caFieldByNid: - return S_ERROR('Distributes name(%s) must be present in CA certificate.' % ', '.join(self.n2fields[nid])) - result = self.__fillX509Name(caFieldByNid[nid], caDict[caFieldByNid[nid]]) - if not result['OK']: - return result - # Test supplied fields + for nid in self.supplied: - if self.defDict.get(self.n2field[nid]): - result = self.__fillX509Name(self.n2field[nid], self.defDict[self.n2field[nid]]) - if not result['OK']: - return result - for nid, value in [(self.fs2nid['CN'], userDict['FullName']), - (self.fs2nid['emailAddress'], userDict['Email'])]: - if nid in self.supplied + self.optional: - result = self.__fillX509Name(self.n2field[nid], value) + if nid not in [self.fields2nid[f] for f in self.dnList]: + return S_ERROR('DNs order list does not contain supplied DN "%s"' % self.nid2field[nid]) + + for field in self.dnList: + values = [] + nid = self.fields2nid[field] + if nid in self.match: + for field in self.nid2fields[nid]: + if field in self.dnInfoDictCA: + values = self.dnInfoDictCA[field] + if not values: + return S_ERROR('Not found "%s" match DN in CA' % field) + for field in self.nid2fields[nid]: + if kwargs.get(field): + values = kwargs[field] if isinstance(kwargs[field], list) else [kwargs[field]] + if not values: + for field in self.nid2fields[nid]: + if self.parameters.get(field): + values = self.parameters[field] + if not values and nid in self.supplied: + return S_ERROR('No values set for "%s" DN' % field) + + for value in values: + result = self.__fillX509Name(field, value) if not result['OK']: return result - + # WARN: This logic not support list of distribtes name elements resDN = m2.x509_name_oneline(self.__X509Name.x509_name) # pylint: disable=no-member - if userDN and not userDN == resDN: - return S_ERROR('%s not matched with created %s' % (userDN, resDN)) + result = self.checkStatus(resDN) + if not result['OK']: + return result return S_OK(resDN) def __parseCACFG(self): @@ -256,8 +259,10 @@ def __parseCACFG(self): """ block = '' self.cfg = {} + self.supplied, self.optional, self.match, self.dnList = [], [], [], [] with open(self.parameters['CAConfigFile'], "r") as caCFG: for line in caCFG: + # Ignore comments line = re.sub(r'#.*', '', line) if re.findall(r"\[([A-Za-z0-9_]+)\]", line.replace(' ', '')): block = ''.join(re.findall(r"\[([A-Za-z0-9_]+)\]", line.replace(' ', ''))) @@ -273,6 +278,10 @@ def __parseCACFG(self): for b in self.cfg: if v in self.cfg[b]: val = val.replace('$' + v, self.cfg[b][v]) + if 'default_ca' in self.cfg.get('ca', {}): + if 'policy' in self.cfg.get(self.cfg['ca']['default_ca'], {}): + if block == self.cfg[self.cfg['ca']['default_ca']]['policy']: + self.dnList.append(field) self.cfg[block][field] = val.strip() self.bits = int(self.cfg['req'].get('default_bits') or self.bits) @@ -280,14 +289,17 @@ def __parseCACFG(self): if not self.parameters.get('CertFile'): self.parameters['CertFile'] = self.cfg[self.cfg['ca']['default_ca']]['certificate'] self.parameters['KeyFile'] = self.cfg[self.cfg['ca']['default_ca']]['private_key'] + # Read distinguished names for k, v in self.cfg[self.cfg[self.cfg['ca']['default_ca']]['policy']].items(): - nid = self.fs2nid[k] - if k + '_default' in self.cfg['req']['distinguished_name']: - self.parameters[nid] = self.cfg['req']['distinguished_name'][k + '_default'] - if k + '_min' in self.cfg['req']['distinguished_name']: - self.minDict[nid] = self.cfg['req']['distinguished_name'][k + '_min'] - if k + '_max' in self.cfg['req']['distinguished_name']: - self.maxDict[nid] = self.cfg['req']['distinguished_name'][k + '_max'] + nid = self.fields2nid[k] + self.parameters[nid], self.minDict[nid], self.maxDict[nid] = [], [], [] + for k in ['0.' + k, k]: + if k + '_default' in self.cfg['req']['distinguished_name']: + self.parameters[nid].append(self.cfg['req']['distinguished_name'][k + '_default']) + if k + '_min' in self.cfg['req']['distinguished_name']: + self.minDict[nid].append(self.cfg['req']['distinguished_name'][k + '_min']) + if k + '_max' in self.cfg['req']['distinguished_name']: + self.maxDict[nid].append(self.cfg['req']['distinguished_name'][k + '_max']) if v == 'supplied': self.supplied.append(nid) elif v == 'optional': @@ -295,6 +307,23 @@ def __parseCACFG(self): elif v == 'match': self.match.append(nid) + def __parseDN(self, dn): + """ Return DN fields + + :param str dn: DN + + :return: list -- contain tuple with positionOfField.fieldName, fieldNID, fieldValue + """ + dnInfoDict = {} + for f, v in [f.split('=') for f in dn.lstrip('/').split('/')]: + if not v: + return S_ERROR('No value set for "%s"' % f) + if f not in dnInfoDict: + dnInfoDict[f] = [v] + else: + dnInfoDict[f].append(v) + return S_OK(dnInfoDict) + def __fillX509Name(self, field, value): """ Fill x509_Name object by M2Crypto @@ -304,17 +333,15 @@ def __fillX509Name(self, field, value): :return: S_OK()/S_ERROR() """ if value and m2.x509_name_set_by_nid(self.__X509Name.x509_name, # pylint: disable=no-member - self.fs2nid[field], value) == 0: + self.fields2nid[field], value) == 0: if not self.__X509Name.add_entry_by_txt(field=field, type=ASN1.MBSTRING_ASC, entry=value, len=-1, loc=-1, set=0) == 1: return S_ERROR('Cannot set "%s" field.' % field) return S_OK() - def __createCertM2Crypto(self, extensions=None): + def __createCertM2Crypto(self): """ Create new certificate for user - :param list extensions: list of X509.new_extension() - :return: S_OK(tuple)/S_ERROR() -- tuple contain certificate and pulic key as strings """ # Create publik key @@ -326,11 +353,9 @@ def __createCertM2Crypto(self, extensions=None): userCert.set_version(2) userCert.set_subject(self.__X509Name) userCert.set_serial_number(int(random.random() * 10 ** 10)) - # Add extensions + # Add extentionals userCert.add_ext(X509.new_extension('basicConstraints', 'CA:' + str(False).upper())) userCert.add_ext(X509.new_extension('extendedKeyUsage', 'clientAuth', critical=1)) - for ext in extensions or []: - userCert.add_ext(ext) # Set livetime validityTime = datetime.timedelta(days=400) notBefore = ASN1.ASN1_UTCTIME() @@ -356,16 +381,12 @@ def __createCertM2Crypto(self, extensions=None): userPubKeyStr = userPubKey.as_pem(cipher=None, callback=util.no_passphrase_callback) return S_OK((userCertStr, userPubKeyStr)) - def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): + def getFakeProxy(self, dn, time, group=None): """ Get fake proxy for tests :param str dn: fake DN :param int time: expired time in a seconds - :param str vo: fake VOMS VO name :param str group: if need to add fake DIRAC group - :param dict kwargs: fake VO description dictionary with possible fields: - - Expired time - - Role, etc.. :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string """ @@ -375,7 +396,7 @@ def getFakeProxy(self, dn, time, vo=None, group=None, **kwargs): if not result['OK']: return result - result = self.__createCertM2Crypto([X509.new_extension('vomsExtensions', vo)] if vo else []) + result = self.__createCertM2Crypto() if result['OK']: certStr, keyStr = result['Value'] chain = X509Chain() diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py new file mode 100644 index 00000000000..55e50d70bc2 --- /dev/null +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -0,0 +1,196 @@ +""" This is a test of the DIRACCAProxyProvider +""" + +# pylint: disable=invalid-name,wrong-import-position,protected-access +import os +import re +import sys +import shutil +import commands +import unittest + +from DIRAC.Core.Base.Script import parseCommandLine +parseCommandLine() + +from DIRAC import gLogger, gConfig, S_OK, S_ERROR +from DIRAC.Core.Utilities.CFG import CFG +from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error +from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory + +# For Jenkins +for f in ['', 'TestCode', os.environ['DIRAC']]: + certsPath = os.path.join(f, 'DIRAC/Core/Security/test/certs') + if os.path.exists(certsPath): + break + +testCAPath = '/tmp/testCA' +testCAConfigFile = os.path.join(testCAPath, 'openssl_config_ca.cnf') + +diracTestCACFG = """ +Resources +{ + ProxyProviders + { + DIRAC_CA + { + ProviderType = DIRACCA + CertFile = %s + KeyFile = %s + Supplied = O, OU, CN + DNOrder = O, OU, CN, emailAddress + Optional = emailAddress + OU = CA + C = DN + O = DIRACCA + } + DIRAC_CA_CFG + { + ProviderType = DIRACCA + CAConfigFile = %s + } + } +} +""" % (os.path.join(certsPath, 'ca/ca.cert.pem'), + os.path.join(certsPath, 'ca/ca.key.pem'), + os.path.join(testCAPath, 'openssl_config_ca.cnf')) + + +class DIRACCAProviderTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.failed = False + + # Add configuration + cfg = CFG() + cfg.loadFromBuffer(diracTestCACFG) + gConfig.loadCFG(cfg) + + shutil.copytree(os.path.join(certsPath, 'ca'), testCAPath) + + # Parse + lines = [] + with open(testCAConfigFile, "r") as caCFG: + for line in caCFG: + if re.findall('=', re.sub(r'#.*', '', line)): + # Ignore comments + field = re.sub(r'#.*', '', line).replace(' ', '').rstrip().split('=')[0] + # Put the right dir + line = 'dir = %s #PUT THE RIGHT DIR HERE!\n' % (testCAPath) if field == 'dir' else line + lines.append(line) + # Write modified conf. file + with open(testCAConfigFile, "w") as caCFG: + caCFG.writelines(lines) + + # Result + status, output = commands.getstatusoutput('ls -al %s' % testCAPath) + if status: + gLogger.error(output) + exit() + gLogger.debug('Test path:\n', output) + + def setUp(self): + gLogger.debug('\n') + if self.failed: + self.fail(self.failed) + + def tearDown(self): + pass + + @classmethod + def tearDownClass(cls): + if os.path.exists(testCAPath): + shutil.rmtree(testCAPath) + + +class testDIRACCAProvider(DIRACCAProviderTestCase): + + def test_getProxy(self): + """ Test 'getProxy' - try to get proxies for different users and check it + """ + def check(proxyStr, proxyProvider, name): + """ Check proxy + + :param str proxyStr: proxy as string + :param str proxyProvider: proxy provider name + :param str name: proxy name + """ + proxyFile = os.path.join(testCAPath, proxyProvider + name.replace(' ', '') + '.pem') + gLogger.info('Check proxy..') + chain = X509Chain() + result = chain.loadProxyFromString(proxyStr) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + for result in [chain.getRemainingSecs(), + chain.getIssuerCert(), + chain.getPKeyObj(), + chain.getCertList(), + chain.getNumCertsInChain(), + chain.generateProxyToString(3600), + chain.generateProxyToFile(proxyFile, 3600), + chain.isProxy(), + chain.isLimitedProxy(), + chain.isValidProxy(), + chain.isVOMS(), + chain.isRFC()]: + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + + for proxyProvider, log in [('DIRAC_CA', 'configuring only in DIRAC CFG'), + ('DIRAC_CA_CFG', 'read configuration file')]: + gLogger.info('\n* Try proxy provider that %s..' % log) + result = ProxyProviderFactory().getProxyProvider(proxyProvider) + self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) + ca = result['Value'] + + gLogger.info('* Get proxy using FullName and Email of user..') + for name, email, res in [('MrUser', 'good@mail.com', True), + ('MrUser_1', 'good_1@mail.com', True), + (False, 'good@mail.com', False), + ('MrUser', False, True)]: + gLogger.info('\nFullName: %s' % name or 'absent', 'Email: %s..' % email or 'absent') + # Create user DN + result = ca.generateDN(FullName=name, Email=email) + text = 'Must be ended %s%s' % ('successful' if res else 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) + if not res: + gLogger.info('Msg: %s' % (result['Message'])) + else: + userDN = result['Value'] + gLogger.info('Created DN:', userDN) + + result = ca.getProxy(userDN) + text = 'Must be ended %s%s' % ('successful' if res else 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) + if not res: + gLogger.info('Msg: %s' % (result['Message'])) + else: + check(result['Value']['proxy'], proxyProvider, name) + + gLogger.info('\n* Get proxy using user DN..') + for dn, name, res in [('/O=DIRAC/OU=DIRAC CA/CN=user_3/emailAddress=some@mail.org', 'user_3', True), + ('/O=Dirac/OU=DIRAC CA/CN=user/emailAddress=some@mail.org', 'user', True), + ('/O=Dirac/OU=Without supplied field/emailAddress=some@mail.org', 'not_suplied', False), + ('/O=Dirac/OU=DIRAC CA/CN=without email', 'no_email', True), + ('/some=bad/DN=', 'badDN', False), + ('/BF=Bad Field/O=IN/CN=DN', 'badField', False), + (False, 'absent', False)]: + gLogger.info('\nDN:', dn or 'absent') + try: + result = ca.getProxy(dn) + except Exception as e: + self.assertFalse(res, e) + text = 'Must be ended %s%s' % ('successful' if res else 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) + if not res: + gLogger.info('Msg: %s' % (result['Message'])) + else: + check(result['Value']['proxy'], proxyProvider, name) + + +if __name__ == '__main__': + suite = unittest.defaultTestLoader.loadTestsFromTestCase(DIRACCAProviderTestCase) + suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testDIRACCAProvider)) + testResult = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not testResult.wasSuccessful()) diff --git a/Resources/ProxyProvider/test/__init__.py b/Resources/ProxyProvider/test/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From d4ea842a69a2d3e4d1360e544b78fe90bf79b7af Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 00:11:22 +0100 Subject: [PATCH 13/52] align proxyDB tests to changes --- tests/Integration/Framework/Test_ProxyDB.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index 63d55c7d166..f6772ab42dc 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -467,9 +467,8 @@ def test_getRemoveProxy(self): self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) gLogger.info('== > %s:' % log) + result = self.createProxy(user, group, time, vo=vo) - if not result['OK']: - result = ca.getFakeProxy(dn, time * 3600, vo=vo, group=group) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) chain = result['Value'][0] @@ -562,8 +561,6 @@ def test_getRemoveProxy(self): for vomsuser in ['user', 'user_1']: # Create proxy with VOMS extension result = self.createProxy(vomsuser, 'group_1', 12, vo='vo_1', role='role_2') - if not result['OK']: - result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=%s' % vomsuser, 12 * 3600, group='group_1', vo='vo_1', role='role_2') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) chain, proxyStr = result['Value'] From 4a7e3e1ae9027412c76eb57ac640019aa6d77b39 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 12:22:59 +0100 Subject: [PATCH 14/52] align proxyDB tests to changes --- tests/Integration/Framework/Test_ProxyDB.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index f6772ab42dc..e79f362f921 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -43,6 +43,7 @@ KeyFile = %s Supplied = C, O, OU, CN Optional = emailAddress + DNOrder = C, O, OU, CN, emailAddress OU = None C = DN O = DIRACCA From 1a567dd28abc8050a07d1d81cd252ed7d8fdf2c5 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 12:37:53 +0100 Subject: [PATCH 15/52] align proxyDB tests to changes --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 54f29866b52..2d139433452 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -245,7 +245,7 @@ def generateDN(self, **kwargs): result = self.__fillX509Name(field, value) if not result['OK']: return result - + # WARN: This logic not support list of distribtes name elements resDN = m2.x509_name_oneline(self.__X509Name.x509_name) # pylint: disable=no-member diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index 55e50d70bc2..ab85f66c96b 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -81,7 +81,7 @@ def setUpClass(cls): # Write modified conf. file with open(testCAConfigFile, "w") as caCFG: caCFG.writelines(lines) - + # Result status, output = commands.getstatusoutput('ls -al %s' % testCAPath) if status: From 75ad865fbda70aac022a994b931a3c7ca770b41b Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 12:55:09 +0100 Subject: [PATCH 16/52] add ignore errors --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 2d139433452..a95234eab4e 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -92,7 +92,7 @@ def setParameters(self, parameters): if self.fields2nid[field] not in self.optional + self.supplied + self.match: del self.parameters[field] elif not isinstance(self.parameters[field], list): - self.parameters[field] = self.parameters[field].replace(', ', ',').split(',') + self.parameters[field] = self.parameters[field].replace(', ', ',').split(',') self.defDict = {} for field, value in parameters.items(): diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index ab85f66c96b..4a7b7221c07 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -10,7 +10,7 @@ import unittest from DIRAC.Core.Base.Script import parseCommandLine -parseCommandLine() +parseCommandLine(ignoreErrors=True) from DIRAC import gLogger, gConfig, S_OK, S_ERROR from DIRAC.Core.Utilities.CFG import CFG @@ -160,7 +160,7 @@ def check(proxyStr, proxyProvider, name): result = ca.getProxy(userDN) text = 'Must be ended %s%s' % ('successful' if res else 'with error', - ': %s' % result.get('Message', 'Error message is absent.')) + ': %s' % result.get('Message', 'Error message is absent.')) self.assertEqual(result['OK'], res, text) if not res: gLogger.info('Msg: %s' % (result['Message'])) From 9d53aaee28b2b00260d89d922cb614e6de4da8bf Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 13:18:56 +0100 Subject: [PATCH 17/52] add path ca --- Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index 4a7b7221c07..3ccf48ae2fe 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -18,7 +18,7 @@ from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory # For Jenkins -for f in ['', 'TestCode', os.environ['DIRAC']]: +for f in ['', 'TestCode', os.environ.get('DIRAC'), '../../../../']: certsPath = os.path.join(f, 'DIRAC/Core/Security/test/certs') if os.path.exists(certsPath): break From 8f3f039bf00154880eb9e949695d40d5fbda6626 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 14:38:53 +0100 Subject: [PATCH 18/52] fix path --- .../test/Test_DIRACCAProxyProvider.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index 3ccf48ae2fe..3f9e2125ad1 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -8,6 +8,7 @@ import shutil import commands import unittest +import tempfile from DIRAC.Core.Base.Script import parseCommandLine parseCommandLine(ignoreErrors=True) @@ -17,13 +18,12 @@ from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory -# For Jenkins -for f in ['', 'TestCode', os.environ.get('DIRAC'), '../../../../']: - certsPath = os.path.join(f, 'DIRAC/Core/Security/test/certs') - if os.path.exists(certsPath): - break -testCAPath = '/tmp/testCA' +thisPath = os.path.dirname(os.path.abspath(__file__)).split('/') +rootPath = thisPath[:len(thisPath) - 3] +certsPath = os.path.join('/'.join(rootPath), 'Core/Security/test/certs') + +testCAPath = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'ca') testCAConfigFile = os.path.join(testCAPath, 'openssl_config_ca.cnf') diracTestCACFG = """ @@ -52,7 +52,7 @@ } """ % (os.path.join(certsPath, 'ca/ca.cert.pem'), os.path.join(certsPath, 'ca/ca.key.pem'), - os.path.join(testCAPath, 'openssl_config_ca.cnf')) + testCAConfigFile) class DIRACCAProviderTestCase(unittest.TestCase): From c663c27cb855611fb47fe2b62c29977ef8ffa3b4 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 15:46:32 +0100 Subject: [PATCH 19/52] fix options --- .../ProxyProvider/DIRACCAProxyProvider.py | 12 ++- .../test/Test_DIRACCAProxyProvider.py | 92 +++++++++++-------- 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index a95234eab4e..3d97cdad07e 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -69,15 +69,21 @@ def setParameters(self, parameters): self.algoritm = parameters['Algoritm'] if 'Match' in parameters: self.match = [] - for field in parameters['Match'].replace(' ', '').split(','): + if not isinstance(parameters['Match'], list): + parameters['Match'] = parameters['Match'].replace(', ', ',').split(',') + for field in parameters['Match']: self.match.append(self.fields2nid[field]) if 'Supplied' in parameters: self.supplied = [] - for field in parameters['Supplied'].replace(' ', '').split(','): + if not isinstance(parameters['Supplied'], list): + parameters['Supplied'] = parameters['Supplied'].replace(', ', ',').split(',') + for field in parameters['Supplied']: self.supplied.append(self.fields2nid[field]) if 'Optional' in parameters: self.optional = [] - for field in parameters['Optional'].replace(' ', '').split(','): + if not isinstance(parameters['Optional'], list): + parameters['Optional'] = parameters['Optional'].replace(', ', ',').split(',') + for field in parameters['Optional']: self.optional.append(self.fields2nid[field]) if 'DNOrder' in parameters: if isinstance(parameters['DNOrder'], list): diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index 3f9e2125ad1..c5a6bfe2d1c 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -14,9 +14,9 @@ parseCommandLine(ignoreErrors=True) from DIRAC import gLogger, gConfig, S_OK, S_ERROR -from DIRAC.Core.Utilities.CFG import CFG +# from DIRAC.Core.Utilities.CFG import CFG from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error -from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory +from DIRAC.Resources.ProxyProvider.DIRACCAProxyProvider import DIRACCAProxyProvider thisPath = os.path.dirname(os.path.abspath(__file__)).split('/') @@ -26,33 +26,46 @@ testCAPath = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'ca') testCAConfigFile = os.path.join(testCAPath, 'openssl_config_ca.cnf') -diracTestCACFG = """ -Resources -{ - ProxyProviders - { - DIRAC_CA - { - ProviderType = DIRACCA - CertFile = %s - KeyFile = %s - Supplied = O, OU, CN - DNOrder = O, OU, CN, emailAddress - Optional = emailAddress - OU = CA - C = DN - O = DIRACCA - } - DIRAC_CA_CFG - { - ProviderType = DIRACCA - CAConfigFile = %s - } - } -} -""" % (os.path.join(certsPath, 'ca/ca.cert.pem'), - os.path.join(certsPath, 'ca/ca.key.pem'), - testCAConfigFile) +diracCADict = {'ProviderType': 'DIRACCA', + 'CertFile': os.path.join(certsPath, 'ca/ca.cert.pem'), + 'KeyFile': os.path.join(certsPath, 'ca/ca.key.pem'), + 'Supplied': ['O', 'OU', 'CN'], + 'Optional': ['emailAddress'], + 'DNOrder': ['O', 'OU', 'CN', 'emailAddress'], + 'OU': 'CA', + 'C': 'DN', + 'O': 'DIRACCA', + 'ProviderName': 'DIRAC_CA'} +diracCAConf = {'ProviderType': 'DIRACCA', + 'CAConfigFile': testCAConfigFile, + 'ProviderName': 'DIRAC_CA_CFG'} +# diracTestCACFG = """ +# Resources +# { +# ProxyProviders +# { +# DIRAC_CA +# { +# ProviderType = DIRACCA +# CertFile = %s +# KeyFile = %s +# Supplied = O, OU, CN +# DNOrder = O, OU, CN, emailAddress +# Optional = emailAddress +# OU = CA +# C = DN +# O = DIRACCA +# } +# DIRAC_CA_CFG +# { +# ProviderType = DIRACCA +# CAConfigFile = %s +# } +# } +# } +# """ % (os.path.join(certsPath, 'ca/ca.cert.pem'), +# os.path.join(certsPath, 'ca/ca.key.pem'), +# testCAConfigFile) class DIRACCAProviderTestCase(unittest.TestCase): @@ -62,9 +75,9 @@ def setUpClass(cls): cls.failed = False # Add configuration - cfg = CFG() - cfg.loadFromBuffer(diracTestCACFG) - gConfig.loadCFG(cfg) + # cfg = CFG() + # cfg.loadFromBuffer(diracTestCACFG) + # gConfig.loadCFG(cfg) shutil.copytree(os.path.join(certsPath, 'ca'), testCAPath) @@ -134,12 +147,14 @@ def check(proxyStr, proxyProvider, name): chain.isRFC()]: self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - for proxyProvider, log in [('DIRAC_CA', 'configuring only in DIRAC CFG'), - ('DIRAC_CA_CFG', 'read configuration file')]: + for proxyProvider, log in [(diracCADict, 'configuring only in DIRAC CFG'), + (diracCAConf, 'read configuration file')]: gLogger.info('\n* Try proxy provider that %s..' % log) - result = ProxyProviderFactory().getProxyProvider(proxyProvider) + ca = DIRACCAProxyProvider() + result = ca.setParameters(proxyProvider) + #result = ProxyProviderFactory().getProxyProvider(proxyProvider) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - ca = result['Value'] + # ca = result['Value'] gLogger.info('* Get proxy using FullName and Email of user..') for name, email, res in [('MrUser', 'good@mail.com', True), @@ -165,7 +180,7 @@ def check(proxyStr, proxyProvider, name): if not res: gLogger.info('Msg: %s' % (result['Message'])) else: - check(result['Value']['proxy'], proxyProvider, name) + check(result['Value']['proxy'], proxyProvider['ProviderName'], name) gLogger.info('\n* Get proxy using user DN..') for dn, name, res in [('/O=DIRAC/OU=DIRAC CA/CN=user_3/emailAddress=some@mail.org', 'user_3', True), @@ -179,6 +194,7 @@ def check(proxyStr, proxyProvider, name): try: result = ca.getProxy(dn) except Exception as e: + result['Message'] = str(e) self.assertFalse(res, e) text = 'Must be ended %s%s' % ('successful' if res else 'with error', ': %s' % result.get('Message', 'Error message is absent.')) @@ -186,7 +202,7 @@ def check(proxyStr, proxyProvider, name): if not res: gLogger.info('Msg: %s' % (result['Message'])) else: - check(result['Value']['proxy'], proxyProvider, name) + check(result['Value']['proxy'], proxyProvider['ProviderName'], name) if __name__ == '__main__': From dd278c3f4b8f426d2d786b43d70a1c09799cb5dc Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sun, 15 Mar 2020 16:11:44 +0100 Subject: [PATCH 20/52] remove comments --- .../test/Test_DIRACCAProxyProvider.py | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index c5a6bfe2d1c..f5a570236fc 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -14,7 +14,6 @@ parseCommandLine(ignoreErrors=True) from DIRAC import gLogger, gConfig, S_OK, S_ERROR -# from DIRAC.Core.Utilities.CFG import CFG from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.DIRACCAProxyProvider import DIRACCAProxyProvider @@ -39,33 +38,6 @@ diracCAConf = {'ProviderType': 'DIRACCA', 'CAConfigFile': testCAConfigFile, 'ProviderName': 'DIRAC_CA_CFG'} -# diracTestCACFG = """ -# Resources -# { -# ProxyProviders -# { -# DIRAC_CA -# { -# ProviderType = DIRACCA -# CertFile = %s -# KeyFile = %s -# Supplied = O, OU, CN -# DNOrder = O, OU, CN, emailAddress -# Optional = emailAddress -# OU = CA -# C = DN -# O = DIRACCA -# } -# DIRAC_CA_CFG -# { -# ProviderType = DIRACCA -# CAConfigFile = %s -# } -# } -# } -# """ % (os.path.join(certsPath, 'ca/ca.cert.pem'), -# os.path.join(certsPath, 'ca/ca.key.pem'), -# testCAConfigFile) class DIRACCAProviderTestCase(unittest.TestCase): @@ -74,11 +46,6 @@ class DIRACCAProviderTestCase(unittest.TestCase): def setUpClass(cls): cls.failed = False - # Add configuration - # cfg = CFG() - # cfg.loadFromBuffer(diracTestCACFG) - # gConfig.loadCFG(cfg) - shutil.copytree(os.path.join(certsPath, 'ca'), testCAPath) # Parse @@ -152,9 +119,7 @@ def check(proxyStr, proxyProvider, name): gLogger.info('\n* Try proxy provider that %s..' % log) ca = DIRACCAProxyProvider() result = ca.setParameters(proxyProvider) - #result = ProxyProviderFactory().getProxyProvider(proxyProvider) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) - # ca = result['Value'] gLogger.info('* Get proxy using FullName and Email of user..') for name, email, res in [('MrUser', 'good@mail.com', True), From 90b9135c63e652aa31fa244af34e886655a41427 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 16 Mar 2020 14:25:37 +0100 Subject: [PATCH 21/52] add more docs --- .../ProxyProvider/DIRACCAProxyProvider.py | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 3d97cdad07e..5deab8880d2 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -1,5 +1,36 @@ """ ProxyProvider implementation for the proxy generation using local (DIRAC) CA credentials + + This class is a simple, limited CA, its main purpose is to generate a simple proxy for DIRAC users + who do not have any certificate register on the fly. + + Required parameters in the DIRAC configuration for its implementation: + { + ProviderType = DIRACCA, + CertFile = , + KeyFile = , + Match = , # For ex.: 'Match = O, OU' + Supplied = , + Optional = , + DNOrder = , # For ex.: 'Match = O, OU, CN, emailAddress' + : , # For ex.: 'OU = CA' + } + + Also, as an additional feature, this class can read properties from a simple openssl CA configuration file. + To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, + the distinguished names order in the created proxy will be the same as in the configuration file policy block. + + The Proxy provider supports the next distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html): + SN(surname) + GN(givenName) + C(countryName) + CN(commonName) + L(localityName) + Email(emailAddress) + O(organizationName) + OU(organizationUnitName) + SP,ST(stateOrProvinceName) + SERIALNUMBER(serialNumber) """ import re @@ -32,14 +63,15 @@ def __init__(self, parameters=None): self.supplied = ['CN'] self.optional = ['C', 'O', 'OU', 'emailAddress'] self.dnList = ['C', 'O', 'OU', 'CN', 'emailAddress'] - # Add not supported distributes names + # Distinguished names self.fields2nid = X509.X509_Name.nid.copy() - self.fields2nid['DC'] = -1 - self.fields2nid['domainComponent'] = -1 - self.fields2nid['organizationalUnitName'] = 18 - self.fields2nid['countryName'] = 14 - self.nid2field = {} # nid: most short or specidied in CS distributes name - self.nid2fields = {} # nid: list of distributes names + self.fields2nid['DC'] = -1 # Add DN that is not liested in X509.X509_Name + self.fields2nid['domainComponent'] = -1 # Add DN description that is not liested in X509.X509_Name + self.fields2nid['organizationalUnitName'] = 18 # Add 'OU' description + self.fields2nid['countryName'] = 14 # Add 'C' description + self.fields2nid['SERIALNUMBER'] = 105 # Add 'SERIALNUMBER' distinguished name + self.nid2field = {} # nid: most short or specidied in CS distinguished name + self.nid2fields = {} # nid: list of distinguished names # Specify standart fields for field in self.fields2nid: if self.fields2nid[field] not in self.nid2fields: @@ -222,7 +254,7 @@ def generateDN(self, **kwargs): kwargs['emailAddress'] = [kwargs['Email']] self.__X509Name = X509.X509_Name() - self.log.info('Creating distributes names chain') + self.log.info('Creating distinguished names chain') for nid in self.supplied: if nid not in [self.fields2nid[f] for f in self.dnList]: From 10ac6b1aed57e2d7f1cbf93ef26291d73da5b56f Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 16 Mar 2020 15:17:54 +0100 Subject: [PATCH 22/52] fix docs --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 5deab8880d2..5d470d60997 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -3,8 +3,9 @@ This class is a simple, limited CA, its main purpose is to generate a simple proxy for DIRAC users who do not have any certificate register on the fly. - - Required parameters in the DIRAC configuration for its implementation: + + Required parameters in the DIRAC configuration for its implementation:: + { ProviderType = DIRACCA, CertFile = , @@ -18,9 +19,10 @@ Also, as an additional feature, this class can read properties from a simple openssl CA configuration file. To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, - the distinguished names order in the created proxy will be the same as in the configuration file policy block. + the distinguished names order in the created proxy will be the same as in the configuration file policy block. + + The Proxy provider supports the next distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html):: - The Proxy provider supports the next distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html): SN(surname) GN(givenName) C(countryName) @@ -31,6 +33,7 @@ OU(organizationUnitName) SP,ST(stateOrProvinceName) SERIALNUMBER(serialNumber) + """ import re From 192600bcc249b7fae70f3d0562affaeb6b3a9778 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 16 Mar 2020 16:01:22 +0100 Subject: [PATCH 23/52] fix docs --- .../ProxyProvider/DIRACCAProxyProvider.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 5d470d60997..3d4a10ab88a 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -6,16 +6,16 @@ Required parameters in the DIRAC configuration for its implementation:: - { - ProviderType = DIRACCA, - CertFile = , - KeyFile = , - Match = , # For ex.: 'Match = O, OU' - Supplied = , - Optional = , - DNOrder = , # For ex.: 'Match = O, OU, CN, emailAddress' - : , # For ex.: 'OU = CA' - } + section:: + + * ProviderType = DIRACCA, + * CertFile = , + * KeyFile = , + * Match = , # For ex.: 'Match = O, OU' + * Supplied = , + * Optional = , + * DNOrder = , # For ex.: 'Match = O, OU, CN, emailAddress' + * : , # For ex.: 'OU = CA' Also, as an additional feature, this class can read properties from a simple openssl CA configuration file. To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, From b34e2e1df8296538e2f148090b2d1983391df581 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 16 Mar 2020 16:25:08 +0100 Subject: [PATCH 24/52] fix docs --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 3d4a10ab88a..fee0876331c 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -7,7 +7,7 @@ Required parameters in the DIRAC configuration for its implementation:: section:: - + * ProviderType = DIRACCA, * CertFile = , * KeyFile = , From 9ae8760f86db84fbecb4caf4fb9ccd329d95c9b7 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 16 Mar 2020 16:26:50 +0100 Subject: [PATCH 25/52] fix docs --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index fee0876331c..ae95e653c79 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -14,7 +14,7 @@ * Match = , # For ex.: 'Match = O, OU' * Supplied = , * Optional = , - * DNOrder = , # For ex.: 'Match = O, OU, CN, emailAddress' + * DNOrder = , # For ex.: 'DNOrder = O, OU, CN, emailAddress' * : , # For ex.: 'OU = CA' Also, as an additional feature, this class can read properties from a simple openssl CA configuration file. From 11f48b9438e77850ebfaf589fa4ef831e89d317b Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 19 Mar 2020 17:17:00 +0100 Subject: [PATCH 26/52] fix __X509Name --- .../ProxyProvider/DIRACCAProxyProvider.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index ae95e653c79..50c488e2f50 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -216,6 +216,10 @@ def checkStatus(self, userDN): if any([True if len(values[i]) < self.minDict[nid][i] else False for i in rangeMin]): return S_ERROR('%s values must be more then %s.' % (err, ', '.join(self.minDict[nid]))) + result = self.__fillX509Name(field, values) + if not result['OK']: + return result + return S_OK({'Status': 'ready'}) def getProxy(self, userDN): @@ -225,6 +229,7 @@ def getProxy(self, userDN): :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a proxy string """ + self.__X509Name = X509.X509_Name() result = self.checkStatus(userDN) if result['OK']: result = self.__createCertM2Crypto() @@ -282,10 +287,9 @@ def generateDN(self, **kwargs): if not values and nid in self.supplied: return S_ERROR('No values set for "%s" DN' % field) - for value in values: - result = self.__fillX509Name(field, value) - if not result['OK']: - return result + result = self.__fillX509Name(field, values) + if not result['OK']: + return result # WARN: This logic not support list of distribtes name elements resDN = m2.x509_name_oneline(self.__X509Name.x509_name) # pylint: disable=no-member @@ -365,19 +369,20 @@ def __parseDN(self, dn): dnInfoDict[f].append(v) return S_OK(dnInfoDict) - def __fillX509Name(self, field, value): + def __fillX509Name(self, field, values): """ Fill x509_Name object by M2Crypto :param str field: DN field name - :param str value: value of field + :param list values: values of field, order important :return: S_OK()/S_ERROR() """ - if value and m2.x509_name_set_by_nid(self.__X509Name.x509_name, # pylint: disable=no-member - self.fields2nid[field], value) == 0: - if not self.__X509Name.add_entry_by_txt(field=field, type=ASN1.MBSTRING_ASC, - entry=value, len=-1, loc=-1, set=0) == 1: - return S_ERROR('Cannot set "%s" field.' % field) + for value in values: + if value and m2.x509_name_set_by_nid(self.__X509Name.x509_name, # pylint: disable=no-member + self.fields2nid[field], value) == 0: + if not self.__X509Name.add_entry_by_txt(field=field, type=ASN1.MBSTRING_ASC, + entry=value, len=-1, loc=-1, set=0) == 1: + return S_ERROR('Cannot set "%s" field.' % field) return S_OK() def __createCertM2Crypto(self): From d50f5cd0190bd5dea23a759cbd32da0a4c36104b Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 19 Mar 2020 17:37:35 +0100 Subject: [PATCH 27/52] fix getFakeProxy --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 50c488e2f50..63b63a4d6e8 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -437,8 +437,13 @@ def getFakeProxy(self, dn, time, group=None): :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string """ self.__X509Name = X509.X509_Name() - for field, value in [field.split('=') for field in dn.lstrip('/').split('/')]: - result = self.__fillX509Name(field, value) + result = self.__parseDN(userDN) + if not result['OK']: + return result + dnInfoDict = result['Value'] + + for field, values in dnInfoDict: + result = self.__fillX509Name(field, values) if not result['OK']: return result From eabcb0d596114ff75b626500b64012e88d34f348 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 19 Mar 2020 17:55:20 +0100 Subject: [PATCH 28/52] fix getFakeProxy --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 63b63a4d6e8..3c38714f3cf 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -437,7 +437,7 @@ def getFakeProxy(self, dn, time, group=None): :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string """ self.__X509Name = X509.X509_Name() - result = self.__parseDN(userDN) + result = self.__parseDN(dn) if not result['OK']: return result dnInfoDict = result['Value'] From 8e22cd5c43949cfefede345c30356fbcfbb5e5e4 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 15:51:39 +0100 Subject: [PATCH 29/52] fix dn parser --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 3c38714f3cf..6fa9f0b1b5f 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -40,6 +40,7 @@ import time import random import datetime +import collections from M2Crypto import m2, util, X509, ASN1, EVP, RSA @@ -359,7 +360,7 @@ def __parseDN(self, dn): :return: list -- contain tuple with positionOfField.fieldName, fieldNID, fieldValue """ - dnInfoDict = {} + dnInfoDict = collections.OrderedDict() for f, v in [f.split('=') for f in dn.lstrip('/').split('/')]: if not v: return S_ERROR('No value set for "%s"' % f) @@ -442,7 +443,7 @@ def getFakeProxy(self, dn, time, group=None): return result dnInfoDict = result['Value'] - for field, values in dnInfoDict: + for field, values in dnInfoDict.items(): result = self.__fillX509Name(field, values) if not result['OK']: return result From 7c1fa9774c38f1ed37424d808f9ba8ca2367591b Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 17:00:48 +0100 Subject: [PATCH 30/52] fix integration test --- .../Test_DIRACCAProxyProvider.py | 129 ++++++------------ 1 file changed, 40 insertions(+), 89 deletions(-) diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index 0d679ba5a94..c54185ef5e7 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -12,11 +12,12 @@ from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory -# For Jenkins -for f in ['', 'TestCode', os.environ['DIRAC']]: - certsPath = os.path.join(f, 'DIRAC/Core/Security/test/certs') - if os.path.exists(certsPath): - break +thisPath = os.path.dirname(os.path.abspath(__file__)).split('/') +rootPath = thisPath[:len(thisPath) - 3] +certsPath = os.path.join('/'.join(rootPath), 'Core/Security/test/certs') + +testCAPath = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'ca') +testCAConfigFile = os.path.join(testCAPath, 'openssl_config_ca.cnf') diracTestCACFG = """ Resources @@ -26,17 +27,19 @@ DIRAC_TEST_CA { ProviderType = DIRACCA - CAConfigFile = %s + CertFile = %s + KeyFile = %s Match = Supplied = C, O, OU, CN Optional = emailAddress + DNOrder = C, O, OU, CN, emailAddress C = FR O = DIRAC OU = DIRAC TEST } } } -""" % os.path.join(certsPath, 'ca/openssl_config_ca.cnf') +""" % (os.path.join(certsPath, 'ca/ca.cert.pem'), os.path.join(certsPath, 'ca/ca.key.pem')) userCFG = """ Registry @@ -69,24 +72,11 @@ class DIRACCAPPTest(unittest.TestCase): @classmethod def setUpClass(cls): - __caPath = os.path.join(certsPath, 'ca') - cls.caConfigFile = os.path.join(__caPath, 'openssl_config_ca.cnf') - - # Save original configuration file - lines = [] - shutil.copyfile(cls.caConfigFile, cls.caConfigFile + 'bak') - with open(cls.caConfigFile, "r") as caCFG: - for line in caCFG: - if re.findall('=', re.sub(r'#.*', '', line)): - field = re.sub(r'#.*', '', line).replace(' ', '').rstrip().split('=')[0] - line = 'dir = %s #PUT THE RIGHT DIR HERE!\n' % (__caPath) if field == 'dir' else line - lines.append(line) - with open(cls.caConfigFile, "w") as caCFG: - caCFG.writelines(lines) + pass @classmethod def tearDownClass(cls): - shutil.move(cls.caConfigFile + 'bak', cls.caConfigFile) + pass def setUp(self): @@ -100,83 +90,44 @@ def setUp(self): self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') self.pp = result['Value'] - self.userDictClean = { - "FullName": "DIRAC test user", - "Email": "testuser@diracgrid.org" - } - self.userDictCleanDN = { - "DN": "/C=FR/O=DIRAC/OU=DIRAC TEST/CN=DIRAC test user/emailAddress=testuser@diracgrid.org", - "Email": "testuser@diracgrid.org" - } - self.userDictGroup = { - "FullName": "DIRAC test user", - "Email": "testuser@diracgrid.org", - "DiracGroup": "dirac_user" - } - def tearDown(self): pass def test_getProxy(self): - - result = self.pp.getProxy(self.userDictClean) + for dn, res in [('/C=FR/O=DIRAC/OU=DIRAC TEST/CN=DIRAC test user/emailAddress=testuser@diracgrid.org', True), + ('/C=FR/OU=DIRAC TEST/emailAddress=testuser@diracgrid.org', False), + ('/C=FR/OU=DIRAC/O=DIRAC TEST/emailAddress=testuser@diracgrid.org', False), + ('/C=FR/O=DIRAC/BADFIELD=DIRAC TEST/CN=DIRAC test user', False)]: + result = self.pp.getProxy(self.userDict['DN']) + text = 'Must be ended %s%s' % ('successful' if res else 'with error', + ': %s' % result.get('Message', 'Error message is absent.')) + self.assertEqual(result['OK'], res, text) + if res: + chain = X509Chain() + chain.loadChainFromString(result['Value']['proxy']) + result = chain.getCredentials() + self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') + credDict = result['Value'] + self.assertEqual(credDict['username'], 'testuser', + '%s, expected %s' % (credDict['username'], 'testuser')) + + def test_generateProxyDN(self): + + userDict = {"FullName": "John Doe", + "Email": "john.doe@nowhere.net", + "O": 'DIRAC', + 'OU': 'DIRAC TEST', + 'C': 'FR'} + result = self.pp.generateDN(userDict) self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - chain = X509Chain() - chain.loadChainFromString(result['Value']['proxy']) - result = chain.getCredentials() - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - credDict = result['Value'] - self.assertEqual(credDict['username'], 'testuser', - '%s, expected %s' % (credDict['username'], 'testuser')) - self.assertEqual(credDict['group'], 'dirac_user', - '%s, expected %s' % (credDict['group'], 'dirac_user')) - - def test_getProxyDN(self): - - result = self.pp.getProxy(self.userDictCleanDN) + result = self.pp.getUserDN(result['Value']) self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') chain = X509Chain() chain.loadChainFromString(result['Value']['proxy']) result = chain.getCredentials() self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - credDict = result['Value'] - self.assertEqual(credDict['username'], 'testuser', - '%s, expected %s' % (credDict['username'], 'testuser')) - self.assertEqual(credDict['group'], 'dirac_user', - '%s, expected %s' % (credDict['group'], 'dirac_user')) - - def test_getProxyGroup(self): - - result = self.pp.getProxy(self.userDictGroup) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - chain = X509Chain() - chain.loadChainFromString(result['Value']['proxy']) - result = chain.getCredentials() - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - credDict = result['Value'] - self.assertEqual(credDict['username'], 'testuser', - '%s, expected %s' % (credDict['username'], 'testuser')) - self.assertEqual(credDict['group'], 'dirac_user', - '%s, expected %s' % (credDict['group'], 'dirac_user')) - - def test_getUserDN(self): - - goodDN = '/C=FR/O=DIRAC/OU=DIRAC TEST/CN=DIRAC test user/emailAddress=testuser@diracgrid.org' - badDN_1 = '/C=FR/OU=DIRAC TEST/CN=DIRAC test user/emailAddress=testuser@diracgrid.org' - badDN_2 = '/C=FR/O=DIRAC/OU=DIRAC TEST/emailAddress=testuser@diracgrid.org' - badDN_3 = '/C=FR/O=DIRAC/OU=DIRAC TEST/CN=DIRAC test user' - result = self.pp.getUserDN(userDN=goodDN) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - result = self.pp.getUserDN(userDN=badDN_1) - self.assertFalse(result['OK'], 'Must be fail.') - result = self.pp.getUserDN(userDN=badDN_2) - self.assertFalse(result['OK'], 'Must be fail.') - result = self.pp.getUserDN(userDN=badDN_3) - self.assertFalse(result['OK'], 'Must be fail.') - userDict = {"FullName": "John Doe", "Email": "john.doe@nowhere.net"} - result = self.pp.getUserDN(userDict) - self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - self.assertEqual(result['Value'], '/C=FR/O=DIRAC/OU=DIRAC TEST/CN=John Doe/emailAddress=john.doe@nowhere.net') + issuer = result['Value']['issuer'] + self.assertEqual(issuer, '/C=FR/O=DIRAC/OU=DIRAC TEST/CN=John Doe/emailAddress=john.doe@nowhere.net') if __name__ == '__main__': From 8dac8c48005990d2807baa252590f3a3f8638181 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 17:14:25 +0100 Subject: [PATCH 31/52] fix integration test --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 6 +++--- .../ProxyProvider/Test_DIRACCAProxyProvider.py | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 6fa9f0b1b5f..1222f35ec80 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -175,7 +175,7 @@ def checkStatus(self, userDN): try: userNIDs = [self.fields2nid[f.split('=')[0]] for f in userDN.lstrip('/').split('/')] - except ValueError as e: + except (ValueError, KeyError) as e: return S_ERROR('Unknown DN field in used DN: %s' % e) nidOrder = [self.fields2nid[f] for f in self.dnList] for index, nid in enumerate(userNIDs): @@ -187,13 +187,13 @@ def checkStatus(self, userDN): try: if userNIDs.index(nidOrder[i]) > index: return S_ERROR('Bad DNs order') - except ValueError: + except (ValueError, KeyError): continue for i in range(nidOrder.index(nid) + 1, len(nidOrder)): try: if userNIDs.index(nidOrder[i]) < index: return S_ERROR('Bad DNs order') - except ValueError: + except (ValueError, KeyError): continue for nid in self.supplied: diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index c54185ef5e7..13f72fbf88e 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -13,12 +13,9 @@ from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory thisPath = os.path.dirname(os.path.abspath(__file__)).split('/') -rootPath = thisPath[:len(thisPath) - 3] +rootPath = thisPath[:len(thisPath) - 4] certsPath = os.path.join('/'.join(rootPath), 'Core/Security/test/certs') -testCAPath = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'ca') -testCAConfigFile = os.path.join(testCAPath, 'openssl_config_ca.cnf') - diracTestCACFG = """ Resources { @@ -98,7 +95,7 @@ def test_getProxy(self): ('/C=FR/OU=DIRAC TEST/emailAddress=testuser@diracgrid.org', False), ('/C=FR/OU=DIRAC/O=DIRAC TEST/emailAddress=testuser@diracgrid.org', False), ('/C=FR/O=DIRAC/BADFIELD=DIRAC TEST/CN=DIRAC test user', False)]: - result = self.pp.getProxy(self.userDict['DN']) + result = self.pp.getProxy(dn) text = 'Must be ended %s%s' % ('successful' if res else 'with error', ': %s' % result.get('Message', 'Error message is absent.')) self.assertEqual(result['OK'], res, text) @@ -118,9 +115,9 @@ def test_generateProxyDN(self): "O": 'DIRAC', 'OU': 'DIRAC TEST', 'C': 'FR'} - result = self.pp.generateDN(userDict) + result = self.pp.generateDN(**userDict) self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') - result = self.pp.getUserDN(result['Value']) + result = self.pp.getProxy(result['Value']) self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') chain = X509Chain() chain.loadChainFromString(result['Value']['proxy']) From b00df3da8aba18b376bb3c5bb0d036dd4bfa9635 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 18:25:23 +0100 Subject: [PATCH 32/52] fix whitespace --- .../Resources/ProxyProvider/Test_DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index 13f72fbf88e..cbfd17f5f28 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -110,7 +110,7 @@ def test_getProxy(self): def test_generateProxyDN(self): - userDict = {"FullName": "John Doe", + userDict = {"FullName": "John Doe", "Email": "john.doe@nowhere.net", "O": 'DIRAC', 'OU': 'DIRAC TEST', From cc5843077d05b4039a12d0e812ef508daac11325 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 19:25:10 +0100 Subject: [PATCH 33/52] use os env --- .../Resources/ProxyProvider/Test_DIRACCAProxyProvider.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index cbfd17f5f28..856e81604b9 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -12,9 +12,7 @@ from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory -thisPath = os.path.dirname(os.path.abspath(__file__)).split('/') -rootPath = thisPath[:len(thisPath) - 4] -certsPath = os.path.join('/'.join(rootPath), 'Core/Security/test/certs') +certsPath = os.path.join(os.environ['DIRAC'], 'Core/Security/test/certs') diracTestCACFG = """ Resources From 3686c8b3cf297e19c388baa9a90edf9e9d65e47d Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 23 Mar 2020 20:10:01 +0100 Subject: [PATCH 34/52] use os env --- .../Resources/ProxyProvider/Test_DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index 856e81604b9..fb76e535106 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -12,7 +12,7 @@ from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory -certsPath = os.path.join(os.environ['DIRAC'], 'Core/Security/test/certs') +certsPath = os.path.join(os.environ['DIRAC'], 'DIRAC/Core/Security/test/certs') diracTestCACFG = """ Resources From b2c563e495377cf0c670c12dc60fa17d7e5b2f16 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 12:18:02 +0200 Subject: [PATCH 35/52] Align PUSP to changes --- .../ProxyProvider/DIRACCAProxyProvider.py | 8 +- Resources/ProxyProvider/PUSPProxyProvider.py | 75 ++++++------------- Resources/ProxyProvider/ProxyProvider.py | 19 +++++ 3 files changed, 46 insertions(+), 56 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 1222f35ec80..32d22f43072 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -103,24 +103,28 @@ def setParameters(self, parameters): self.bits = int(parameters['Bits']) if 'Algoritm' in parameters: self.algoritm = parameters['Algoritm'] + if 'Match' in parameters: self.match = [] if not isinstance(parameters['Match'], list): parameters['Match'] = parameters['Match'].replace(', ', ',').split(',') for field in parameters['Match']: self.match.append(self.fields2nid[field]) + if 'Supplied' in parameters: self.supplied = [] if not isinstance(parameters['Supplied'], list): parameters['Supplied'] = parameters['Supplied'].replace(', ', ',').split(',') for field in parameters['Supplied']: self.supplied.append(self.fields2nid[field]) + if 'Optional' in parameters: self.optional = [] if not isinstance(parameters['Optional'], list): parameters['Optional'] = parameters['Optional'].replace(', ', ',').split(',') for field in parameters['Optional']: self.optional.append(self.fields2nid[field]) + if 'DNOrder' in parameters: if isinstance(parameters['DNOrder'], list): self.dnList = parameters['DNOrder'] @@ -244,9 +248,7 @@ def getProxy(self, userDN): if result['OK']: result = chain.generateProxyToString(365 * 24 * 3600, rfc=True) - if not result['OK']: - return result - return S_OK({'proxy': result['Value']}) + return S_OK({'proxy': result['Value']}) if result['OK'] else result def generateDN(self, **kwargs): """ Get DN of the user certificate that will be created diff --git a/Resources/ProxyProvider/PUSPProxyProvider.py b/Resources/ProxyProvider/PUSPProxyProvider.py index bb42ad1bcdf..73fbefc2ca2 100644 --- a/Resources/ProxyProvider/PUSPProxyProvider.py +++ b/Resources/ProxyProvider/PUSPProxyProvider.py @@ -18,49 +18,35 @@ def __init__(self, parameters=None): super(PUSPProxyProvider, self).__init__(parameters) - def getProxy(self, userDict): - """ Generate user proxy + def checkStatus(self, userDN): + """ Read ready to work status of proxy provider - :param dict userDict: user description dictionary with possible fields: - FullName, UserName, DN, Email, DiracGroup + :param str userDN: user DN - :return: S_OK(basestring)/S_ERROR() -- basestring is a proxy string + :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: + - 'Status' with ready to work status[ready, needToAuth] """ + if not userDN.split(":")[-1]: + return S_ERROR('Can not found user label for DN: %s' % userDN) + if not puspServiceURL: + return S_ERROR('Can not determine PUSP service URL') - userDN = userDict.get('DN') - if not userDN: - return S_ERROR('Incomplete user information') + return S_OK({'Status': 'ready'}) - diracGroup = userDict.get('DiracGroup') - if not diracGroup: - return S_ERROR('Incomplete user information') + def getProxy(self, userDN): + """ Generate user proxy - result = Registry.getGroupsForDN(userDN) + :param str userDN: user DN + + :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a proxy string + """ + result = self.checkStatus(userDN) if not result['OK']: return result - validGroups = result['Value'] - if diracGroup not in validGroups: - return S_ERROR('Invalid group %s for user' % diracGroup) - - voName = Registry.getVOForGroup(diracGroup) - if not voName: - return S_ERROR('Can not determine VO for group %s' % diracGroup) - - csVOMSMapping = Registry.getVOMSAttributeForGroup(diracGroup) - if not csVOMSMapping: - return S_ERROR("No VOMS mapping defined for group %s in the CS" % diracGroup) - vomsAttribute = csVOMSMapping - vomsVO = Registry.getVOMSVOForGroup(diracGroup) - - puspServiceURL = self.parameters.get('ServiceURL') - if not puspServiceURL: - return S_ERROR('Can not determine PUSP service URL for VO %s' % voName) - - user = userDN.split(":")[-1] - - puspURL = "%s?voms=%s:%s&proxy-renewal=false&disable-voms-proxy=false" \ - "&rfc-proxy=true&cn-label=user:%s" % (puspServiceURL, vomsVO, vomsAttribute, user) + puspURL = self.parameters.get('ServiceURL') + puspURL += "?proxy-renewal=false&disable-voms-proxy=true&rfc-proxy=true" + puspURL += "&cn-label=user:%s" % userDN.split(":")[-1] try: proxy = urlopen(puspURL).read() @@ -77,24 +63,7 @@ def getProxy(self, userDict): credDict = result['Value'] if credDict['identity'] != userDN: return S_ERROR('Requested DN does not match the obtained one in the PUSP proxy') - timeLeft = credDict['secondsLeft'] - - result = chain.generateProxyToString(lifeTime=timeLeft, - diracGroup=diracGroup) - if not result['OK']: - return result - proxyString = result['Value'] - return S_OK((proxyString, timeLeft)) - - def getUserDN(self, userDict): - """ Get DN of the user certificate that will be created - - :param dict userDict: - :return: S_OK/S_ERROR, Value is the DN string - """ - userDN = userDict.get('DN') - if not userDN: - return S_ERROR('Incomplete user information') + result = chain.generateProxyToString(lifeTime=credDict['secondsLeft']) - return S_OK(userDN) + return S_OK({'proxy': result['Value']}) if result['OK'] else result diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index 615683d6483..d9ddd450a74 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -16,3 +16,22 @@ def __init__(self, parameters=None): def setParameters(self, parameters): self.parameters = parameters self.name = parameters.get('ProxyProviderName') + + def checkStatus(self, userDN): + """ Read ready to work status of proxy provider + + :param str userDN: user DN + + :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: + - 'Status' with ready to work status[ready, needToAuth] + """ + return return S_OK({'Status': 'ready'}) + + def generateDN(self, **kwargs): + """ Generate new DN + + :param dict kwargs: user description dictionary + + :return: S_OK(str)/S_ERROR() -- contain DN + """ + return S_ERROR('%s work only with ready user DN.') From 963579400187a78555e3db0805f1a8a18aa8c507 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 14:45:26 +0200 Subject: [PATCH 36/52] optimize --- .../ProxyProvider/DIRACCAProxyProvider.py | 88 ++++++------------- 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 32d22f43072..088e2c73f23 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -74,18 +74,10 @@ def __init__(self, parameters=None): self.fields2nid['organizationalUnitName'] = 18 # Add 'OU' description self.fields2nid['countryName'] = 14 # Add 'C' description self.fields2nid['SERIALNUMBER'] = 105 # Add 'SERIALNUMBER' distinguished name - self.nid2field = {} # nid: most short or specidied in CS distinguished name self.nid2fields = {} # nid: list of distinguished names # Specify standart fields - for field in self.fields2nid: - if self.fields2nid[field] not in self.nid2fields: - self.nid2fields[self.fields2nid[field]] = [] - self.nid2fields[self.fields2nid[field]].append(field) - for nid in self.nid2fields: - for field in self.nid2fields[nid]: - if nid not in self.nid2field: - self.nid2field[nid] = field - self.nid2field[nid] = len(field) < len(self.nid2field[nid]) and field or self.nid2field[nid] + for field, nid in self.fields2nid.items(): + self.nid2fields.setdefault(nid, []).append(field) self.dnInfoDictCA = {} def setParameters(self, parameters): @@ -95,61 +87,37 @@ def setParameters(self, parameters): :return: S_OK()/S_ERROR() """ - # If CA configuration file exist + for k, v in parameters: + if not isinstance(v, list) and k in ['Match', 'Supplied', 'Optional', 'DNOrder'] + self.fields2nid: + parameters[k] = v.replace(', ', ',').split(',') self.parameters = parameters + # If CA configuration file exist if parameters.get('CAConfigFile'): self.__parseCACFG() if 'Bits' in parameters: self.bits = int(parameters['Bits']) if 'Algoritm' in parameters: self.algoritm = parameters['Algoritm'] - if 'Match' in parameters: - self.match = [] - if not isinstance(parameters['Match'], list): - parameters['Match'] = parameters['Match'].replace(', ', ',').split(',') - for field in parameters['Match']: - self.match.append(self.fields2nid[field]) - + self.match = [self.fields2nid[f] for f in parameters['Match']] if 'Supplied' in parameters: - self.supplied = [] - if not isinstance(parameters['Supplied'], list): - parameters['Supplied'] = parameters['Supplied'].replace(', ', ',').split(',') - for field in parameters['Supplied']: - self.supplied.append(self.fields2nid[field]) - + self.supplied = [self.fields2nid[f] for f in parameters['Supplied']] if 'Optional' in parameters: - self.optional = [] - if not isinstance(parameters['Optional'], list): - parameters['Optional'] = parameters['Optional'].replace(', ', ',').split(',') - for field in parameters['Optional']: - self.optional.append(self.fields2nid[field]) - + self.optional = [self.fields2nid[f] for f in parameters['Optional']] if 'DNOrder' in parameters: - if isinstance(parameters['DNOrder'], list): - self.dnList = parameters['DNOrder'] - else: - self.dnList = parameters['DNOrder'].replace(', ', ',').split(',') + self.dnList = [] + if not any([any([f in parameters['DNOrder'] for f in self.nid2fields[n]]) for n in self.optional + self.supplied + self.match]): + return S_ERROR('DNOrder must contain all configured fields.') + for field in parameters['DNOrder']: + if self.fields2nid[field] in self.optional + self.supplied + self.match: + self.dnList.append(parameters['DNOrder']) # Set defaults for distridutes names + self.nid2defField = {} for field, value in self.parameters.items(): - if field not in self.fields2nid: - continue - if self.fields2nid[field] not in self.optional + self.supplied + self.match: - del self.parameters[field] - elif not isinstance(self.parameters[field], list): - self.parameters[field] = self.parameters[field].replace(', ', ',').split(',') - - self.defDict = {} - for field, value in parameters.items(): - if field in self.fields2nid: - self.defDict[field] = value - self.defFieldByNid = dict([[self.fields2nid[field], field] for field in self.defDict]) - for nid in self.nid2field: - if nid in self.defFieldByNid: - self.nid2field[nid] = self.defFieldByNid[nid] - self.match.sort() - self.supplied.sort() + if field in self.fields2nid and self.fields2nid[field] in self.optional + self.supplied + self.match: + self.parameters[self.fields2nid[field]] = value + self.nid2defField[self.fields2nid[field]] = field # Read CA certificate chain = X509Chain() @@ -184,7 +152,8 @@ def checkStatus(self, userDN): nidOrder = [self.fields2nid[f] for f in self.dnList] for index, nid in enumerate(userNIDs): if nid not in nidOrder: - return S_ERROR('"%s" field not found in order.' % self.nid2field[nid]) + return S_ERROR('"%s" field not found in order.' % + self.nid2defField.get(nid, min(self.nid2fields[nid], key=len))) if index > nidOrder.index(nid): return S_ERROR('Bad DNs order') for i in range(nidOrder.index(nid) - 1): @@ -202,7 +171,8 @@ def checkStatus(self, userDN): for nid in self.supplied: if nid not in [self.fields2nid[f] for f in dnInfoDict]: - return S_ERROR('Current DN is invalid, "%s" field must be set.' % self.nid2field[nid]) + return S_ERROR('Current DN is invalid, "%s" field must be set.' % + self.nid2defField.get(nid, min(self.nid2fields[nid], key=len))) for field, values in dnInfoDict.items(): nid = self.fields2nid[field] @@ -269,7 +239,8 @@ def generateDN(self, **kwargs): for nid in self.supplied: if nid not in [self.fields2nid[f] for f in self.dnList]: - return S_ERROR('DNs order list does not contain supplied DN "%s"' % self.nid2field[nid]) + return S_ERROR('DNs order list does not contain supplied DN "%s"' % + self.nid2defField.get(nid, min(self.nid2fields[nid], key=len))) for field in self.dnList: values = [] @@ -283,12 +254,11 @@ def generateDN(self, **kwargs): for field in self.nid2fields[nid]: if kwargs.get(field): values = kwargs[field] if isinstance(kwargs[field], list) else [kwargs[field]] - if not values: - for field in self.nid2fields[nid]: - if self.parameters.get(field): - values = self.parameters[field] if not values and nid in self.supplied: - return S_ERROR('No values set for "%s" DN' % field) + # Search default value + if nid not in self.nid2defField: + return S_ERROR('No values set for "%s" DN' % min(self.nid2fields[nid], key=len)) + values = self.parameters[nid] result = self.__fillX509Name(field, values) if not result['OK']: From f309cd626779458f5fd99092c331506500ba3ab8 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 16:06:33 +0200 Subject: [PATCH 37/52] add C field to test --- .../ProxyProvider/DIRACCAProxyProvider.py | 6 +- .../test/Test_DIRACCAProxyProvider.py | 5 +- tests/Integration/Framework/Test_ProxyDB.py | 80 ++++++++++--------- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 088e2c73f23..68b79199734 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -87,8 +87,8 @@ def setParameters(self, parameters): :return: S_OK()/S_ERROR() """ - for k, v in parameters: - if not isinstance(v, list) and k in ['Match', 'Supplied', 'Optional', 'DNOrder'] + self.fields2nid: + for k, v in parameters.items(): + if not isinstance(v, list) and k in ['Match', 'Supplied', 'Optional', 'DNOrder'] + self.fields2nid.keys(): parameters[k] = v.replace(', ', ',').split(',') self.parameters = parameters # If CA configuration file exist @@ -110,7 +110,7 @@ def setParameters(self, parameters): return S_ERROR('DNOrder must contain all configured fields.') for field in parameters['DNOrder']: if self.fields2nid[field] in self.optional + self.supplied + self.match: - self.dnList.append(parameters['DNOrder']) + self.dnList.append(field) # Set defaults for distridutes names self.nid2defField = {} diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index f5a570236fc..697e553a43c 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -10,10 +10,7 @@ import unittest import tempfile -from DIRAC.Core.Base.Script import parseCommandLine -parseCommandLine(ignoreErrors=True) - -from DIRAC import gLogger, gConfig, S_OK, S_ERROR +from DIRAC import gLogger from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.Resources.ProxyProvider.DIRACCAProxyProvider import DIRACCAProxyProvider diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index e79f362f921..1b7b4ae8ea3 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -21,12 +21,7 @@ from DIRAC.FrameworkSystem.DB.ProxyDB import ProxyDB from DIRAC.Resources.ProxyProvider.DIRACCAProxyProvider import DIRACCAProxyProvider -# For Jenkins -for f in ['', 'TestCode', os.environ['DIRAC']]: - certsPath = os.path.join(f, 'DIRAC/Core/Security/test/certs') - if os.path.exists(certsPath): - break - +certsPath = os.path.join(os.environ['DIRAC'], 'DIRAC/Core/Security/test/certs') ca = DIRACCAProxyProvider() ca.setParameters({'CertFile': os.path.join(certsPath, 'ca/ca.cert.pem'), 'KeyFile': os.path.join(certsPath, 'ca/ca.key.pem')}) @@ -72,10 +67,10 @@ } user { - DN = /O=DN/O=DIRAC/CN=user + DN = /C=CC/O=DN/O=DIRAC/CN=user DNProperties { - -O_DN-O_DIRAC-OU_user + -C_CC-O_DN-O_DIRAC-CN_user { ProxyProviders = Groups = dirac_user @@ -84,10 +79,10 @@ } user_1 { - DN = /O=DN/O=DIRAC/CN=user_1 + DN = /C=CC/O=DN/O=DIRAC/CN=user_1 DNProperties { - -O_DN-O_DIRAC-OU_user_1 + -C_CC-O_DN-O_DIRAC-CN_user_1 { ProxyProviders = Groups = dirac_user @@ -96,22 +91,22 @@ } user_2 { - DN = /O=DN/O=DIRAC/CN=user_2 + DN = /C=CC/O=DN/O=DIRAC/CN=user_2 DNProperties { - -O_DN-O_DIRAC-OU_user_2 + -C_CC-O_DN-O_DIRAC-CN_user_2 { } } } user_3 { - DN = /O=DN/O=DIRAC/CN=user_3 + DN = /C=CC/O=DN/O=DIRAC/CN=user_3 } # Not in dirac_user group user_4 { - DN = /O=DN/O=DIRAC/CN=user_4 + DN = /C=CC/O=DN/O=DIRAC/CN=user_4 } } Groups @@ -148,6 +143,14 @@ class ProxyDBTestCase(unittest.TestCase): @classmethod def createProxy(self, userName, group, time, vo=None, role=None): """ Create user proxy + + :param str userName: user name + :param str group: group name + :param int time: proxy expired time + :param str vo: VOMS VO name + :param str role: VOMS Role + + :return: S_OK(tuple)/S_ERROR() -- contain proxy as and as string """ userCertFile = os.path.join(self.userDir, userName + '.cert.pem') userKeyFile = os.path.join(self.userDir, userName + '.key.pem') @@ -258,6 +261,7 @@ def setUpClass(cls): prompt = no req_extensions = v3_req [ req_dn ] + C = CC O = DN 0.O = DIRAC CN = %s @@ -341,7 +345,7 @@ def test_connectDB(self): def test_getUsers(self): """ Test 'getUsers' - try to get users from DB """ - field = '("%%s", "/O=DN/O=DIRAC/CN=%%s", %%s "PEM", TIMESTAMPADD(SECOND, %%s, UTC_TIMESTAMP()))%s' % '' + field = '("%%s", "/C=CC/O=DN/O=DIRAC/CN=%%s", %%s "PEM", TIMESTAMPADD(SECOND, %%s, UTC_TIMESTAMP()))%s' % '' # Fill table for test gLogger.info('\n* Fill tables for test..') for table, values, fields in [('ProxyDB_Proxies', @@ -374,7 +378,7 @@ def test_purgeExpiredProxies(self): # Purge existed proxies gLogger.info('\n* First cleaning..') cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' + cmd += '("user", "/C=CC/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, -1, UTC_TIMESTAMP()));' result = db._query(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) @@ -396,22 +400,22 @@ def test_getRemoveProxy(self): gLogger.info('* Check posible crashes when get proxy..') # Make record with not valid proxy, valid group, user and short expired time cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("user", "/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' + cmd += '("user", "/C=CC/O=DN/O=DIRAC/CN=user", "group_1", "PEM", ' cmd += 'TIMESTAMPADD(SECOND, 1800, UTC_TIMESTAMP()));' result = db._update(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Try to no correct getProxy requests - for dn, group, reqtime, log in [('/O=DN/O=DIRAC/CN=user', 'group_1', 9999, + for dn, group, reqtime, log in [('/C=CC/O=DN/O=DIRAC/CN=user', 'group_1', 9999, 'No proxy provider, set request time, not valid proxy in ProxyDB_Proxies'), - ('/O=DN/O=DIRAC/CN=user', 'group_1', 0, + ('/C=CC/O=DN/O=DIRAC/CN=user', 'group_1', 0, 'Not valid proxy in ProxyDB_Proxies'), - ('/O=DN/O=DIRAC/CN=no_user', 'no_valid_group', 0, + ('/C=CC/O=DN/O=DIRAC/CN=no_user', 'no_valid_group', 0, 'User not exist, proxy not in DB tables'), - ('/O=DN/O=DIRAC/CN=user', 'no_valid_group', 0, + ('/C=CC/O=DN/O=DIRAC/CN=user', 'no_valid_group', 0, 'Group not valid, proxy not in DB tables'), - ('/O=DN/O=DIRAC/CN=user', 'group_1', 0, + ('/C=CC/O=DN/O=DIRAC/CN=user', 'group_1', 0, 'No proxy provider for user, proxy not in DB tables'), - ('/O=DN/O=DIRAC/CN=user_4', 'group_2', 0, + ('/C=CC/O=DN/O=DIRAC/CN=user_4', 'group_2', 0, 'Group has option enableToDownload = False in CS')]: gLogger.info('== > %s:' % log) result = db.getProxy(dn, group, reqtime) @@ -449,17 +453,17 @@ def test_getRemoveProxy(self): self.assertTrue(bool(int(result['Value']['TotalRecords']) == 0), 'In DB present proxies.') gLogger.info('* Upload proxy..') - for user, dn, group, vo, time, res, log in [("user", '/O=DN/O=DIRAC/CN=user', "group_1", False, 12, + for user, dn, group, vo, time, res, log in [("user", '/C=CC/O=DN/O=DIRAC/CN=user', "group_1", False, 12, True, 'With group extension'), - ("user", '/O=DN/O=DIRAC/CN=user', False, "vo_1", 12, + ("user", '/C=CC/O=DN/O=DIRAC/CN=user', False, "vo_1", 12, False, 'With voms extension'), - ("user_1", '/O=DN/O=DIRAC/CN=user_1', False, "vo_1", 12, + ("user_1", '/C=CC/O=DN/O=DIRAC/CN=user_1', False, "vo_1", 12, False, 'With voms extension'), - ("user", '/O=DN/O=DIRAC/CN=user', False, False, 0, + ("user", '/C=CC/O=DN/O=DIRAC/CN=user', False, False, 0, False, 'Expired proxy'), - ("no_user", '/O=DN/O=DIRAC/CN=no_user', False, False, 12, + ("no_user", '/C=CC/O=DN/O=DIRAC/CN=no_user', False, False, 12, False, 'Not exist user'), - ("user", '/O=DN/O=DIRAC/CN=user', False, False, 12, + ("user", '/C=CC/O=DN/O=DIRAC/CN=user', False, False, 12, True, 'Valid proxy')]: for table in ['ProxyDB_Proxies', 'ProxyDB_CleanProxies']: result = db._update('DELETE FROM %s WHERE UserName = "user"' % table) @@ -511,7 +515,7 @@ def test_getRemoveProxy(self): (False, 'group_2', 0, 'Request group not contain user'), (True, 'group_1', 0, 'Request time less that in stored proxy')]: gLogger.info('== > %s:' % log) - result = db.getProxy('/O=DN/O=DIRAC/CN=user', group, reqtime) + result = db.getProxy('/C=CC/O=DN/O=DIRAC/CN=user', group, reqtime) text = 'Must be ended %s%s' % (res and 'successful' or 'with error', ': %s' % result.get('Message', 'Error message is absent.')) self.assertEqual(result['OK'], res, text) @@ -525,7 +529,7 @@ def test_getRemoveProxy(self): gLogger.info('Msg: %s' % (result['Message'])) gLogger.info('* Check that DB is clean..') - result = db.deleteProxy('/O=DN/O=DIRAC/CN=user', proxyProvider='Certificate') + result = db.deleteProxy('/C=CC/O=DN/O=DIRAC/CN=user', proxyProvider='Certificate') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) @@ -533,7 +537,7 @@ def test_getRemoveProxy(self): gLogger.info('* Get proxy when it store only in ProxyDB_Proxies..') # Make record with proxy that contain group - result = ca.getFakeProxy('/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1') + result = ca.getFakeProxy('/C=CC/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) proxyStr = result['Value'][1] cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' @@ -552,7 +556,7 @@ def test_getRemoveProxy(self): self.assertEqual('group_1', result['Value'], 'Group must be group_1, not ' + result['Value']) gLogger.info('* Check that DB is clean..') - result = db.deleteProxy('/O=DN/O=DIRAC/CN=user') + result = db.deleteProxy('/C=CC/O=DN/O=DIRAC/CN=user') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) result = db.getProxiesContent({'UserName': ['user_ca', 'user', 'user_1', 'user_2', 'user_3']}, {}) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) @@ -569,17 +573,17 @@ def test_getRemoveProxy(self): self.assertTrue(bool(chain.isVOMS().get('Value')), 'Cannot create proxy with VOMS extension') cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' - cmd += '("%s", "/O=DN/O=DIRAC/CN=%s", "group_1", "%s", ' % (vomsuser, vomsuser, proxyStr) + cmd += '("%s", "/C=CC/O=DN/O=DIRAC/CN=%s", "group_1", "%s", ' % (vomsuser, vomsuser, proxyStr) cmd += 'TIMESTAMPADD(SECOND, 43200, UTC_TIMESTAMP()))' result = db._update(cmd) self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) # Try to get proxy with VOMS extension - for dn, group, role, time, log in [('/O=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, + for dn, group, role, time, log in [('/C=CC/O=DN/O=DIRAC/CN=user_4', 'group_2', False, 9999, 'Not exist VO for current group'), - ('/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, + ('/C=CC/O=DN/O=DIRAC/CN=user', 'group_1', 'role_1', 9999, 'Stored proxy already have different VOMS extension'), - ('/O=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, + ('/C=CC/O=DN/O=DIRAC/CN=user_1', 'group_1', 'role_1', 9999, 'Stored proxy already have different VOMS extension'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'group_1', 'role_1', 9999, 'Not correct VO configuration')]: @@ -596,7 +600,7 @@ def test_getRemoveProxy(self): self.assertTrue(bool(db._query(cmd)['Value'][0][0] == count)) gLogger.info('* Delete proxies..') - for dn, table in [('/O=DN/O=DIRAC/CN=user', 'ProxyDB_Proxies'), + for dn, table in [('/C=CC/O=DN/O=DIRAC/CN=user', 'ProxyDB_Proxies'), ('/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org', 'ProxyDB_CleanProxies')]: result = db.deleteProxy(dn) From 4f398150e2f48bb5a9e6a03ceecc676e4e63c3cd Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 16:13:09 +0200 Subject: [PATCH 38/52] fix bug --- Resources/ProxyProvider/ProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index d9ddd450a74..534a4d65329 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -25,7 +25,7 @@ def checkStatus(self, userDN): :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: - 'Status' with ready to work status[ready, needToAuth] """ - return return S_OK({'Status': 'ready'}) + return S_OK({'Status': 'ready'}) def generateDN(self, **kwargs): """ Generate new DN From d80e76a35402979d8f1ca13bdba394b34498a614 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 17:59:42 +0200 Subject: [PATCH 39/52] whitespace --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 7 ++++--- Resources/ProxyProvider/ProxyProvider.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 68b79199734..9e246e572cb 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -104,18 +104,19 @@ def setParameters(self, parameters): self.supplied = [self.fields2nid[f] for f in parameters['Supplied']] if 'Optional' in parameters: self.optional = [self.fields2nid[f] for f in parameters['Optional']] + allFields = self.optional + self.supplied + self.match if 'DNOrder' in parameters: self.dnList = [] - if not any([any([f in parameters['DNOrder'] for f in self.nid2fields[n]]) for n in self.optional + self.supplied + self.match]): + if not any([any([f in parameters['DNOrder'] for f in self.nid2fields[n]]) for n in allFields]): return S_ERROR('DNOrder must contain all configured fields.') for field in parameters['DNOrder']: - if self.fields2nid[field] in self.optional + self.supplied + self.match: + if self.fields2nid[field] in allFields: self.dnList.append(field) # Set defaults for distridutes names self.nid2defField = {} for field, value in self.parameters.items(): - if field in self.fields2nid and self.fields2nid[field] in self.optional + self.supplied + self.match: + if field in self.fields2nid and self.fields2nid[field] in allFields: self.parameters[self.fields2nid[field]] = value self.nid2defField[self.fields2nid[field]] = field diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index 534a4d65329..eb9c7faf8de 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -26,7 +26,7 @@ def checkStatus(self, userDN): - 'Status' with ready to work status[ready, needToAuth] """ return S_OK({'Status': 'ready'}) - + def generateDN(self, **kwargs): """ Generate new DN From 3ece313609851f33a2a8ab4835b6ec0d89dc85bb Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 19:30:19 +0200 Subject: [PATCH 40/52] fix parsing --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 9e246e572cb..89ce66b3b1c 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -312,7 +312,7 @@ def __parseCACFG(self): for k, v in self.cfg[self.cfg[self.cfg['ca']['default_ca']]['policy']].items(): nid = self.fields2nid[k] self.parameters[nid], self.minDict[nid], self.maxDict[nid] = [], [], [] - for k in ['0.' + k, k]: + for k in ['%s.%s' % (i, k) for i in range(0,10)] + [k]: if k + '_default' in self.cfg['req']['distinguished_name']: self.parameters[nid].append(self.cfg['req']['distinguished_name'][k + '_default']) if k + '_min' in self.cfg['req']['distinguished_name']: From 9719ba5630eefb1a38c746f4d6a46a4e7b90bf19 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 20:02:03 +0200 Subject: [PATCH 41/52] whitespace --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 89ce66b3b1c..d10382c6385 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -312,7 +312,7 @@ def __parseCACFG(self): for k, v in self.cfg[self.cfg[self.cfg['ca']['default_ca']]['policy']].items(): nid = self.fields2nid[k] self.parameters[nid], self.minDict[nid], self.maxDict[nid] = [], [], [] - for k in ['%s.%s' % (i, k) for i in range(0,10)] + [k]: + for k in ['%s.%s' % (i, k) for i in range(0, 5)] + [k]: if k + '_default' in self.cfg['req']['distinguished_name']: self.parameters[nid].append(self.cfg['req']['distinguished_name'][k + '_default']) if k + '_min' in self.cfg['req']['distinguished_name']: From d1d3c33d47a0d84762518d24734d2d85141ddb89 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 20:15:48 +0200 Subject: [PATCH 42/52] fix S_OK/S_ERROR --- Resources/ProxyProvider/ProxyProvider.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index eb9c7faf8de..c00b7b0f04d 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -1,5 +1,6 @@ """ ProxyProvider base class for various proxy providers """ +from DIRAC import S_OK, S_ERROR __RCSID__ = "$Id$" From ae6da26b6339fdef0f90badffc9d6b5d824c9f54 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Thu, 2 Apr 2020 21:45:18 +0200 Subject: [PATCH 43/52] fix pusp --- Resources/ProxyProvider/PUSPProxyProvider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/ProxyProvider/PUSPProxyProvider.py b/Resources/ProxyProvider/PUSPProxyProvider.py index 73fbefc2ca2..a889b6b2e26 100644 --- a/Resources/ProxyProvider/PUSPProxyProvider.py +++ b/Resources/ProxyProvider/PUSPProxyProvider.py @@ -28,7 +28,7 @@ def checkStatus(self, userDN): """ if not userDN.split(":")[-1]: return S_ERROR('Can not found user label for DN: %s' % userDN) - if not puspServiceURL: + if not self.parameters.get('ServiceURL'): return S_ERROR('Can not determine PUSP service URL') return S_OK({'Status': 'ready'}) @@ -44,7 +44,7 @@ def getProxy(self, userDN): if not result['OK']: return result - puspURL = self.parameters.get('ServiceURL') + puspURL = self.parameters['ServiceURL'] puspURL += "?proxy-renewal=false&disable-voms-proxy=true&rfc-proxy=true" puspURL += "&cn-label=user:%s" % userDN.split(":")[-1] From 4b2b547ed6aba6f644504cb274c42f7e0c2e2373 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 3 Apr 2020 00:21:34 +0200 Subject: [PATCH 44/52] _forceGenerateProxyForDN for internal use --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index d10382c6385..ea436b95c2a 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -401,14 +401,16 @@ def __createCertM2Crypto(self): userPubKeyStr = userPubKey.as_pem(cipher=None, callback=util.no_passphrase_callback) return S_OK((userCertStr, userPubKeyStr)) - def getFakeProxy(self, dn, time, group=None): - """ Get fake proxy for tests + def _forceGenerateProxyForDN(self, dn, time, group=None): + """ An additional helper method for creating a proxy without any substantial validation, + it can be used for a specific case(such as testing) where just need to generate a proxy + with specific DN on the fly. - :param str dn: fake DN + :param str dn: requested proxy DN :param int time: expired time in a seconds - :param str group: if need to add fake DIRAC group + :param str group: if need to add DIRAC group - :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a fake proxy string + :return: S_OK(tuple)/S_ERROR() -- contain proxy as chain and as string """ self.__X509Name = X509.X509_Name() result = self.__parseDN(dn) From 1cd78806aa05a999eb0acc8c93ccd88cb17d9d9d Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 3 Apr 2020 00:26:56 +0200 Subject: [PATCH 45/52] _forceGenerateProxyForDN for internal use --- tests/Integration/Framework/Test_ProxyDB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index 1b7b4ae8ea3..f501e5c16ca 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -537,7 +537,7 @@ def test_getRemoveProxy(self): gLogger.info('* Get proxy when it store only in ProxyDB_Proxies..') # Make record with proxy that contain group - result = ca.getFakeProxy('/C=CC/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1') + result = ca._forceGenerateProxyForDN('/C=CC/O=DN/O=DIRAC/CN=user', 12 * 3600, group='group_1') self.assertTrue(result['OK'], '\n' + result.get('Message', 'Error message is absent.')) proxyStr = result['Value'][1] cmd = 'INSERT INTO ProxyDB_Proxies(UserName, UserDN, UserGroup, Pem, ExpirationTime) VALUES ' From dd08fb1de8ee602335538cd5153bfbfb6ec19150 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 3 Apr 2020 19:11:27 +0200 Subject: [PATCH 46/52] simplifying return --- FrameworkSystem/DB/ProxyDB.py | 2 +- Resources/ProxyProvider/PUSPProxyProvider.py | 12 +++++------- Resources/ProxyProvider/ProxyProvider.py | 11 +++++------ .../ProxyProvider/test/Test_DIRACCAProxyProvider.py | 4 ++-- .../ProxyProvider/Test_DIRACCAProxyProvider.py | 4 ++-- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/FrameworkSystem/DB/ProxyDB.py b/FrameworkSystem/DB/ProxyDB.py index 122fc26889a..97f42cd3ad0 100755 --- a/FrameworkSystem/DB/ProxyDB.py +++ b/FrameworkSystem/DB/ProxyDB.py @@ -717,7 +717,7 @@ def __generateProxyFromProxyProvider(self, userDN, proxyProvider): result = pp.getProxy(userDN) if not result['OK']: return result - proxyStr = result['Value']['proxy'] + proxyStr = result['Value'] chain = X509Chain() result = chain.loadProxyFromString(proxyStr) if not result['OK']: diff --git a/Resources/ProxyProvider/PUSPProxyProvider.py b/Resources/ProxyProvider/PUSPProxyProvider.py index a889b6b2e26..88c53833a16 100644 --- a/Resources/ProxyProvider/PUSPProxyProvider.py +++ b/Resources/ProxyProvider/PUSPProxyProvider.py @@ -23,22 +23,22 @@ def checkStatus(self, userDN): :param str userDN: user DN - :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: - - 'Status' with ready to work status[ready, needToAuth] + :return: S_OK()/S_ERROR() """ + # Search a unique identifier(username) to use as cn-label to generate PUSP if not userDN.split(":")[-1]: return S_ERROR('Can not found user label for DN: %s' % userDN) if not self.parameters.get('ServiceURL'): return S_ERROR('Can not determine PUSP service URL') - return S_OK({'Status': 'ready'}) + return S_OK() def getProxy(self, userDN): """ Generate user proxy :param str userDN: user DN - :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a proxy string + :return: S_OK()/S_ERROR() -- contain a proxy string """ result = self.checkStatus(userDN) if not result['OK']: @@ -64,6 +64,4 @@ def getProxy(self, userDN): if credDict['identity'] != userDN: return S_ERROR('Requested DN does not match the obtained one in the PUSP proxy') - result = chain.generateProxyToString(lifeTime=credDict['secondsLeft']) - - return S_OK({'proxy': result['Value']}) if result['OK'] else result + return chain.generateProxyToString(lifeTime=credDict['secondsLeft']) diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index c00b7b0f04d..07f64a8c97a 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -12,21 +12,20 @@ def __init__(self, parameters=None): self.parameters = parameters self.name = None if parameters: - self.name = parameters.get('ProxyProviderName') + self.name = parameters.get('ProviderName') def setParameters(self, parameters): self.parameters = parameters - self.name = parameters.get('ProxyProviderName') + self.name = parameters.get('ProviderName') def checkStatus(self, userDN): """ Read ready to work status of proxy provider :param str userDN: user DN - :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: - - 'Status' with ready to work status[ready, needToAuth] + :return: S_OK()/S_ERROR() """ - return S_OK({'Status': 'ready'}) + return S_OK() def generateDN(self, **kwargs): """ Generate new DN @@ -35,4 +34,4 @@ def generateDN(self, **kwargs): :return: S_OK(str)/S_ERROR() -- contain DN """ - return S_ERROR('%s work only with ready user DN.') + return S_ERROR('%s work only with ready user DN.' % self.name) diff --git a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py index 697e553a43c..e5fa9806b68 100644 --- a/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/test/Test_DIRACCAProxyProvider.py @@ -142,7 +142,7 @@ def check(proxyStr, proxyProvider, name): if not res: gLogger.info('Msg: %s' % (result['Message'])) else: - check(result['Value']['proxy'], proxyProvider['ProviderName'], name) + check(result['Value'], proxyProvider['ProviderName'], name) gLogger.info('\n* Get proxy using user DN..') for dn, name, res in [('/O=DIRAC/OU=DIRAC CA/CN=user_3/emailAddress=some@mail.org', 'user_3', True), @@ -164,7 +164,7 @@ def check(proxyStr, proxyProvider, name): if not res: gLogger.info('Msg: %s' % (result['Message'])) else: - check(result['Value']['proxy'], proxyProvider['ProviderName'], name) + check(result['Value'], proxyProvider['ProviderName'], name) if __name__ == '__main__': diff --git a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py index fb76e535106..af066179249 100644 --- a/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py +++ b/tests/Integration/Resources/ProxyProvider/Test_DIRACCAProxyProvider.py @@ -99,7 +99,7 @@ def test_getProxy(self): self.assertEqual(result['OK'], res, text) if res: chain = X509Chain() - chain.loadChainFromString(result['Value']['proxy']) + chain.loadChainFromString(result['Value']) result = chain.getCredentials() self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') credDict = result['Value'] @@ -118,7 +118,7 @@ def test_generateProxyDN(self): result = self.pp.getProxy(result['Value']) self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') chain = X509Chain() - chain.loadChainFromString(result['Value']['proxy']) + chain.loadChainFromString(result['Value']) result = chain.getCredentials() self.assertTrue(result['OK'], '\n%s' % result.get('Message') or 'Error message is absent.') issuer = result['Value']['issuer'] From a753c8ef38aaf838ec483c17ba45e7714d1f627f Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Fri, 3 Apr 2020 19:29:17 +0200 Subject: [PATCH 47/52] simplifying return --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 9 ++++----- Resources/ProxyProvider/PUSPProxyProvider.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index ea436b95c2a..a3761e3ad2b 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -137,8 +137,7 @@ def checkStatus(self, userDN): :param str userDN: user DN - :return: S_OK(dict)/S_ERROR() -- dictionary contain fields: - - 'Status' with ready to work status[ready, needToAuth] + :return: S_OK()/S_ERROR() """ self.log.debug('Ckecking work status of', self.parameters['ProviderName']) result = self.__parseDN(userDN) @@ -196,14 +195,14 @@ def checkStatus(self, userDN): if not result['OK']: return result - return S_OK({'Status': 'ready'}) + return S_OK() def getProxy(self, userDN): """ Generate user proxy :param str userDN: user DN - :return: S_OK(dict)/S_ERROR() -- dict contain 'proxy' field with is a proxy string + :return: S_OK(str)/S_ERROR() -- contain a proxy string """ self.__X509Name = X509.X509_Name() result = self.checkStatus(userDN) @@ -219,7 +218,7 @@ def getProxy(self, userDN): if result['OK']: result = chain.generateProxyToString(365 * 24 * 3600, rfc=True) - return S_OK({'proxy': result['Value']}) if result['OK'] else result + return result def generateDN(self, **kwargs): """ Get DN of the user certificate that will be created diff --git a/Resources/ProxyProvider/PUSPProxyProvider.py b/Resources/ProxyProvider/PUSPProxyProvider.py index 88c53833a16..324fc496a2e 100644 --- a/Resources/ProxyProvider/PUSPProxyProvider.py +++ b/Resources/ProxyProvider/PUSPProxyProvider.py @@ -38,7 +38,7 @@ def getProxy(self, userDN): :param str userDN: user DN - :return: S_OK()/S_ERROR() -- contain a proxy string + :return: S_OK(str)/S_ERROR() -- contain a proxy string """ result = self.checkStatus(userDN) if not result['OK']: From 9bbd1fdfedd1f05c0a5c937920be7e1307376efa Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sat, 4 Apr 2020 15:38:11 +0200 Subject: [PATCH 48/52] fix error message in ProxyProvider --- Resources/ProxyProvider/ProxyProvider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/ProxyProvider.py b/Resources/ProxyProvider/ProxyProvider.py index 07f64a8c97a..cd494966818 100644 --- a/Resources/ProxyProvider/ProxyProvider.py +++ b/Resources/ProxyProvider/ProxyProvider.py @@ -34,4 +34,4 @@ def generateDN(self, **kwargs): :return: S_OK(str)/S_ERROR() -- contain DN """ - return S_ERROR('%s work only with ready user DN.' % self.name) + return S_ERROR("Not implemented in %s", self.name) From b3da8c36b55cb9fe9c37e4dcab83fd56b732866c Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sat, 4 Apr 2020 16:52:37 +0200 Subject: [PATCH 49/52] some fixes --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index a3761e3ad2b..4d92937963e 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -21,7 +21,7 @@ To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, the distinguished names order in the created proxy will be the same as in the configuration file policy block. - The Proxy provider supports the next distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html):: + The Proxy provider supports the following distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html):: SN(surname) GN(givenName) @@ -363,7 +363,7 @@ def __createCertM2Crypto(self): :return: S_OK(tuple)/S_ERROR() -- tuple contain certificate and pulic key as strings """ - # Create publik key + # Create public key userPubKey = EVP.PKey() userPubKey.assign_rsa(RSA.gen_key(self.bits, 65537, util.quiet_genparam_callback)) # Create certificate From 73d45fa3a58085507f993f848bd22054eabd0580 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Sat, 4 Apr 2020 19:40:13 +0200 Subject: [PATCH 50/52] some fixes --- Resources/ProxyProvider/DIRACCAProxyProvider.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 4d92937963e..0dc8b965cbe 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -21,7 +21,8 @@ To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, the distinguished names order in the created proxy will be the same as in the configuration file policy block. - The Proxy provider supports the following distinguished names(https://www.cryptosys.net/pki/manpki/pki_distnames.html):: + The Proxy provider supports the following distinguished names + (https://www.cryptosys.net/pki/manpki/pki_distnames.html):: SN(surname) GN(givenName) From ffba7fff8febf2bcd6f533601c0c146429772c71 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Mon, 6 Apr 2020 10:41:20 +0200 Subject: [PATCH 51/52] docs --- .../ProxyProvider/DIRACCAProxyProvider.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Resources/ProxyProvider/DIRACCAProxyProvider.py b/Resources/ProxyProvider/DIRACCAProxyProvider.py index 0dc8b965cbe..1ee4319efe4 100644 --- a/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -6,16 +6,16 @@ Required parameters in the DIRAC configuration for its implementation:: - section:: - - * ProviderType = DIRACCA, - * CertFile = , - * KeyFile = , - * Match = , # For ex.: 'Match = O, OU' - * Supplied = , - * Optional = , - * DNOrder = , # For ex.: 'DNOrder = O, OU, CN, emailAddress' - * : , # For ex.: 'OU = CA' + section: + + * ProviderType = DIRACCA, + * CertFile = , + * KeyFile = , + * Match = , # For ex.: 'Match = O, OU' + * Supplied = , + * Optional = , + * DNOrder = , # For ex.: 'DNOrder = O, OU, CN, emailAddress' + * : , # For ex.: 'OU = CA' Also, as an additional feature, this class can read properties from a simple openssl CA configuration file. To do this, just set the path to an existing configuration file as a CAConfigFile parameter. In this case, From 19c2d5a401cb49f972ffd7cdc810df609aeadae3 Mon Sep 17 00:00:00 2001 From: TaykYoku Date: Tue, 7 Apr 2020 16:09:46 +0200 Subject: [PATCH 52/52] use exception log --- Core/Security/ProxyInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Security/ProxyInfo.py b/Core/Security/ProxyInfo.py index fcf56b5eac2..3ac8fcce3ce 100644 --- a/Core/Security/ProxyInfo.py +++ b/Core/Security/ProxyInfo.py @@ -160,7 +160,7 @@ def formatProxyStepsInfoAsString(infoList): try: value = base64.b16encode(value) except Exception as e: - gLogger.error('Cannot read serial:', e) + gLogger.exception("Could not read serial:", lException=e) if key == 'lifetime': secs = value hours = int(secs / 3600)