From e4a032d6f3c835267dd88476c1edf4cb6a6080a9 Mon Sep 17 00:00:00 2001 From: Federico Stagni Date: Wed, 8 Apr 2026 11:12:43 +0200 Subject: [PATCH] fix: delete proxy does not need a group --- src/DIRAC/FrameworkSystem/DB/ProxyDB.py | 47 +++++------- .../Service/ProxyManagerHandler.py | 17 ++--- .../scripts/dirac_proxy_destroy.py | 74 ++----------------- tests/Integration/Framework/Test_ProxyDB.py | 18 ++--- 4 files changed, 41 insertions(+), 115 deletions(-) diff --git a/src/DIRAC/FrameworkSystem/DB/ProxyDB.py b/src/DIRAC/FrameworkSystem/DB/ProxyDB.py index f26194de659..dd7e18b06e6 100755 --- a/src/DIRAC/FrameworkSystem/DB/ProxyDB.py +++ b/src/DIRAC/FrameworkSystem/DB/ProxyDB.py @@ -1,13 +1,14 @@ -""" ProxyDB class is a front-end to the ProxyDB MySQL database. +"""ProxyDB class is a front-end to the ProxyDB MySQL database. - Database contains the following tables: +Database contains the following tables: - * ProxyDB_Requests -- a delegation requests storage table for a given proxy Chain - * ProxyDB_CleanProxies -- table for storing proxies in "clean" form, ie without - the presence of DIRAC and VOMS extensions. - * ProxyDB_VOMSProxies -- proxy storage table with VOMS extension already added. - * ProxyDB_Log -- table with logs. +* ProxyDB_Requests -- a delegation requests storage table for a given proxy Chain +* ProxyDB_CleanProxies -- table for storing proxies in "clean" form, ie without + the presence of DIRAC and VOMS extensions. +* ProxyDB_VOMSProxies -- proxy storage table with VOMS extension already added. +* ProxyDB_Log -- table with logs. """ + import textwrap from threading import Lock @@ -167,8 +168,7 @@ def generateDelegationRequest(self, proxyChain, userDN): data = retVal["Value"] if not data: return S_ERROR("Insertion of the request in the db didn't work as expected") - userGroup = proxyChain.getDIRACGroup().get("Value") or "unset" - self.logAction("request upload", userDN, userGroup, userDN, "any") + self.logAction("request upload", userDN, userDN) # Here we go! return S_OK({"id": data[0][0], "request": reqStr}) @@ -249,8 +249,7 @@ def completeDelegation(self, requestId, userDN, delegatedPem): return self.deleteRequest(requestId) if retVal["OK"] else retVal def __storeProxy(self, userDN, chain, proxyProvider=None): - """Store user proxy into the Proxy repository for a user specified by his - DN and group or proxy provider. + """Store user proxy into the Proxy repository for a user specified by their DN :param str userDN: user DN from proxy :param X509Chain() chain: proxy chain @@ -353,7 +352,7 @@ def __storeProxy(self, userDN, chain, proxyProvider=None): sqlSet.append(f"{k} = {dValues[k]}") cmd = f"UPDATE `{sTable}` SET {', '.join(sqlSet)} WHERE {' AND '.join(sqlWhere)}" - self.logAction("store proxy", userDN, proxyProvider, userDN, proxyProvider) + self.logAction("store proxy", userDN, userDN) return self._update(cmd) def purgeExpiredProxies(self, sendNotifications=True): @@ -376,23 +375,17 @@ def purgeExpiredProxies(self, sendNotifications=True): return result return S_OK(purged) - def deleteProxy(self, userDN, userGroup=None, proxyProvider=None): + def deleteProxy(self, userDN): """Remove proxy of the given user from the repository :param str userDN: user DN - :param str userGroup: DIRAC group - :param str proxyProvider: proxy provider name :return: S_OK()/S_ERROR() """ try: userDN = self._escapeString(userDN)["Value"] - if userGroup: - userGroup = self._escapeString(userGroup)["Value"] - if proxyProvider: - proxyProvider = self._escapeString(proxyProvider)["Value"] except KeyError: - return S_ERROR("Invalid DN or group or proxy provider") + return S_ERROR("Invalid DN") errMsgs = [] req = f"DELETE FROM `ProxyDB_CleanProxies` WHERE UserDN={userDN}" result = self._update(req) @@ -552,7 +545,7 @@ def __getProxyFromProxyProviders(self, userDN, userGroup, requiredLifeTime): result = chain.generateProxyToString(remainingSecs, diracGroup=userGroup) if result["OK"]: return S_OK((result["Value"], remainingSecs)) - errMsgs.append(f"\"{proxyProvider}\": {result['Message']}") + errMsgs.append(f'"{proxyProvider}": {result["Message"]}') return S_ERROR("Cannot generate proxy%s" % (errMsgs and ": " + ", ".join(errMsgs) or "")) @@ -592,8 +585,8 @@ def getProxy(self, userDN, userGroup, requiredLifeTime=None): # Proxy is invalid for some reason, let's delete it if not chain.isValidProxy()["OK"]: - self.deleteProxy(userDN, userGroup) - return S_ERROR(DErrno.EPROXYFIND, f"{userDN}@{userGroup} has no proxy registered") + self.deleteProxy(userDN) + return S_ERROR(DErrno.EPROXYFIND, f"{userDN} has no proxy registered") return S_OK((chain, timeLeft)) def __getVOMSAttribute(self, userGroup, requiredVOMSAttribute=False): @@ -824,27 +817,23 @@ def getProxiesContent(self, selDict, sortList, start=0, limit=0): totalRecords = len(data) return S_OK({"ParameterNames": fields, "Records": data, "TotalRecords": totalRecords}) - def logAction(self, action, issuerDN, issuerGroup, targetDN, targetGroup): + def logAction(self, action, issuerDN, targetDN): """Add an action to the log :param str action: proxy action :param str issuerDN: user DN of issuer - :param str issuerGroup: DIRAC group of issuer :param str targetDN: user DN of target - :param str targetGroup: DIRAC group of target :return: S_ERROR() """ try: sAction = self._escapeString(action)["Value"] sIssuerDN = self._escapeString(issuerDN)["Value"] - sIssuerGroup = self._escapeString(issuerGroup)["Value"] sTargetDN = self._escapeString(targetDN)["Value"] - sTargetGroup = self._escapeString(targetGroup)["Value"] except KeyError: return S_ERROR("Can't escape from death") cmd = "INSERT INTO `ProxyDB_Log` ( Action, IssuerDN, IssuerGroup, TargetDN, TargetGroup, Timestamp ) VALUES " - cmd += f"( {sAction}, {sIssuerDN}, {sIssuerGroup}, {sTargetDN}, {sTargetGroup}, UTC_TIMESTAMP() )" + cmd += f"( {sAction}, {sIssuerDN}, 'IssuerGroup' {sTargetDN}, 'TargetGroup', UTC_TIMESTAMP() )" retVal = self._update(cmd) if not retVal["OK"]: self.log.error("Can't add a proxy action log: ", retVal["Message"]) diff --git a/src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py b/src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py index 035760ea531..dd135016217 100644 --- a/src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py +++ b/src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py @@ -102,7 +102,7 @@ def export_completeDelegationUpload(self, requestId, pemChain): :return: S_OK(dict)/S_ERROR() -- dict contain proxies """ credDict = self.getRemoteCredentials() - userId = f'{credDict["username"]}:{credDict["group"]}' + userId = f"{credDict['username']}:{credDict['group']}" retVal = self.__proxyDB.completeDelegation(requestId, credDict["DN"], pemChain) if not retVal["OK"]: gLogger.error("Upload proxy failed", f"id: {requestId} user: {userId} message: {retVal['Message']}") @@ -240,7 +240,7 @@ def __getVOMSProxy(self, userDN, userGroup, requestPem, requiredLifetime, vomsAt requiredLifetime = int(min(secsLeft, requiredLifetime * self.__maxExtraLifeFactor)) return chain.generateChainFromRequestString(requestPem, lifetime=requiredLifetime, requireLimited=forceLimited) - types_deleteProxyBundle = [(list, tuple)] + types_deleteProxyBundle = [list] def export_deleteProxyBundle(self, idList): """delete a list of id's @@ -252,9 +252,7 @@ def export_deleteProxyBundle(self, idList): errorInDelete = [] deleted = 0 for _id in idList: - if len(_id) != 2: - errorInDelete.append(f"{str(_id)} doesn't have two fields") - retVal = self.export_deleteProxy(_id[0], _id[1]) + retVal = self.export_deleteProxy(_id) if not retVal["OK"]: errorInDelete.append(f"{str(_id)} : {retVal['Message']}") else: @@ -263,13 +261,12 @@ def export_deleteProxyBundle(self, idList): return S_ERROR(f"Could not delete some proxies: {','.join(errorInDelete)}") return S_OK(deleted) - types_deleteProxy = [(list, tuple)] + types_deleteProxy = [str] - def export_deleteProxy(self, userDN, userGroup): + def export_deleteProxy(self, userDN): """Delete a proxy from the DB :param str userDN: user DN - :param str userGroup: DIRAC group :return: S_OK()/S_ERROR() """ @@ -277,10 +274,10 @@ def export_deleteProxy(self, userDN, userGroup): if Properties.PROXY_MANAGEMENT not in credDict["properties"]: if userDN != credDict["DN"]: return S_ERROR("You aren't allowed!") - retVal = self.__proxyDB.deleteProxy(userDN, userGroup) + retVal = self.__proxyDB.deleteProxy(userDN) if not retVal["OK"]: return retVal - self.__proxyDB.logAction("delete proxy", credDict["DN"], credDict["group"], userDN, userGroup) + self.__proxyDB.logAction("delete proxy", credDict["DN"], userDN) return S_OK() types_getContents = [dict, (list, tuple), int, int] diff --git a/src/DIRAC/FrameworkSystem/scripts/dirac_proxy_destroy.py b/src/DIRAC/FrameworkSystem/scripts/dirac_proxy_destroy.py index 2fd8e6a5719..4e9a41f9bbb 100755 --- a/src/DIRAC/FrameworkSystem/scripts/dirac_proxy_destroy.py +++ b/src/DIRAC/FrameworkSystem/scripts/dirac_proxy_destroy.py @@ -5,12 +5,11 @@ Example: $ dirac-proxy-destroy -a """ + import os import DIRAC from DIRAC import S_OK, gLogger -from DIRAC.ConfigurationSystem.Client.Helpers import Registry -from DIRAC.Core.Base.Client import Client from DIRAC.Core.Base.Script import Script from DIRAC.Core.Security import Locations, ProxyInfo from DIRAC.FrameworkSystem.Client.ProxyManagerClient import gProxyManager @@ -25,16 +24,8 @@ def __init__(self): """ creates a Params class with default values """ - self.vos = [] self.delete_all = False - def addVO(self, voname): - """ - adds a VO to be deleted from remote proxies - """ - self.vos.append(voname) - return S_OK() - def setDeleteAll(self, _): """ deletes local and remote proxies @@ -46,7 +37,7 @@ def needsValidProxy(self): """ returns true if any remote operations are required """ - return self.vos or self.delete_all + return self.delete_all # note the magic : and = def registerCLISwitches(self): @@ -56,49 +47,19 @@ def registerCLISwitches(self): Script.registerSwitch( "a", "all", "Delete the local and all uploaded proxies (the nuclear option)", self.setDeleteAll ) - Script.registerSwitch("v:", "vo=", "Delete uploaded proxy for vo name given", self.addVO) - - -def getProxyGroups(): - """ - Returns a set of all remote proxy groups stored on the dirac server for the user invoking the command. - """ - proxies = gProxyManager.getUserProxiesInfo() - if not proxies["OK"]: - raise RuntimeError("Could not retrieve uploaded proxy info.") - - user_groups = set() - for dn in proxies["Value"]: - dn_groups = set(proxies["Value"][dn].keys()) - user_groups.update(dn_groups) - return user_groups - -def mapVoToGroups(voname): - """ - Returns all groups available for a given VO as a set. - """ - - vo_dict = Registry.getGroupsForVO(voname) - if not vo_dict["OK"]: - raise RuntimeError(f"Could not retrieve groups for vo {voname}.") - - return set(vo_dict["Value"]) - - -def deleteRemoteProxy(userdn, vogroup): +def deleteRemoteProxy(userdn): """ Deletes proxy for a vogroup for the user envoking this function. Returns a list of all deleted proxies (if any). """ - rpcClient = Client(url="Framework/ProxyManager") - retVal = rpcClient.deleteProxyBundle([(userdn, vogroup)]) + retVal = gProxyManager.deleteProxyBundle([(userdn)]) if retVal["OK"]: - gLogger.notice(f"Deleted proxy for {vogroup}.") + gLogger.notice("Deleted proxy.") else: - gLogger.error(f"Failed to delete proxy for {vogroup}.") + gLogger.error("Failed to delete proxy.") def deleteLocalProxy(proxyLoc): @@ -123,7 +84,7 @@ def run(): Script.parseCommandLine(ignoreErrors=True) - if options.delete_all and options.vos: + if options.delete_all: gLogger.error("-a and -v options are mutually exclusive. Please pick one or the other.") return 1 @@ -142,27 +103,8 @@ def run(): userDN = result["Value"]["identity"] + deleteRemoteProxy(userDN) if options.delete_all: - # delete remote proxies - remote_groups = getProxyGroups() - if not remote_groups: - gLogger.notice("No remote proxies found.") - for vo_group in remote_groups: - deleteRemoteProxy(userDN, vo_group) - # delete local proxy - deleteLocalProxy(proxyLoc) - elif options.vos: - vo_groups = set() - for voname in options.vos: - vo_groups.update(mapVoToGroups(voname)) - # filter set of all groups to only contain groups for which there is a user proxy - user_groups = getProxyGroups() - vo_groups.intersection_update(user_groups) - if not vo_groups: - gLogger.notice("You have no proxies registered for any of the specified VOs.") - for group in vo_groups: - deleteRemoteProxy(userDN, group) - else: deleteLocalProxy(proxyLoc) return 0 diff --git a/tests/Integration/Framework/Test_ProxyDB.py b/tests/Integration/Framework/Test_ProxyDB.py index c4c88e3bf9d..493d9df5a31 100644 --- a/tests/Integration/Framework/Test_ProxyDB.py +++ b/tests/Integration/Framework/Test_ProxyDB.py @@ -1,6 +1,7 @@ -""" This is a test of the ProxyDB - It supposes that the DB is present and installed in DIRAC +"""This is a test of the ProxyDB +It supposes that the DB is present and installed in DIRAC """ + # pylint: disable=invalid-name,wrong-import-position,protected-access import os import re @@ -19,7 +20,6 @@ DIRAC.initialize(require_auth=False, host_credentials=True) # Initialize configuration -import DIRAC from DIRAC import gLogger, gConfig, S_OK, S_ERROR from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error from DIRAC.FrameworkSystem.DB.ProxyDB import ProxyDB @@ -39,8 +39,8 @@ DIRAC_CA {{ ProviderType = DIRACCA - CertFile = {os.path.join(certsPath, 'ca/ca.cert.pem')} - KeyFile = {os.path.join(certsPath, 'ca/ca.key.pem')} + CertFile = {os.path.join(certsPath, "ca/ca.cert.pem")} + KeyFile = {os.path.join(certsPath, "ca/ca.key.pem")} Supplied = C, O, OU, CN Optional = emailAddress DNOrder = C, O, OU, CN, emailAddress @@ -391,7 +391,7 @@ def test_purgeExpiredProxies(self): def test_getRemoveProxy(self): """Testing get, store proxy""" gLogger.info("\n* Check that DB is clean..") - result = db.getProxiesContent({"UserName": ["user_ca", "user", "user_1" "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.") @@ -441,9 +441,7 @@ def test_getRemoveProxy(self): ) 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" - ) + result = db.deleteProxy("/C=DN/O=DIRACCA/OU=None/CN=user_ca/emailAddress=user_ca@diracgrid.org") 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.")) @@ -523,7 +521,7 @@ def test_getRemoveProxy(self): gLogger.info(f"Msg: {result['Message']}") gLogger.info("* Check that DB is clean..") - result = db.deleteProxy("/C=CC/O=DN/O=DIRAC/CN=user", proxyProvider="Certificate") + 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_2", "user_3"]}, {}) self.assertTrue(result["OK"], "\n" + result.get("Message", "Error message is absent."))