diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 5354f5470ec..0f3346fbede 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -13,10 +13,7 @@ jobs: matrix: command: - pytest - - DIRAC_USE_M2CRYPTO=No pytest - # Security tests are flakey due to reference counting bugs in pyGSI/src/crypto/asn1.c - - pytest Core/Security/test || (echo "Retrying..."; pytest Core/Security/test) || (echo "Retrying again..."; pytest Core/Security/test) - - DIRAC_USE_M2CRYPTO=No pytest Core/Security/test || (echo "Retrying..."; DIRAC_USE_M2CRYPTO=No pytest Core/Security/test) || (echo "Retrying again..."; DIRAC_USE_M2CRYPTO=No pytest Core/Security/test) + - pytest Core/Security/test - tests/checkDocs.sh # TODO This should cover more than just tests/CI # Excluded codes related to sourcing files diff --git a/Core/DISET/private/SSLSocketFactory.py b/Core/DISET/private/SSLSocketFactory.py deleted file mode 100644 index b516fd95422..00000000000 --- a/Core/DISET/private/SSLSocketFactory.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -This module seemd to be used only by HTTPDISETConnection, which was removed in v7r0. -It will be removed in v7r1 - -""" -__RCSID__ = "$Id$" - -from DIRAC import S_OK, S_ERROR, gConfig -from DIRAC.Core.DISET.private.Transports.SSL.FakeSocket import FakeSocket -from DIRAC.Core.DISET.private.Transports.SSL.pygsi.SocketInfoFactory import gSocketInfoFactory -from DIRAC.Core.DISET.private.Transports.SSLTransport import checkSanity - - -class SSLSocketFactory: - - KW_USE_CERTIFICATES = "useCertificates" - KW_PROXY_LOCATION = "proxyLocation" - KW_PROXY_STRING = "proxyString" - KW_PROXY_CHAIN = "proxyChain" - KW_SKIP_CA_CHECK = "skipCACheck" - KW_TIMEOUT = "timeout" - KW_ENABLE_SESSIONS = 'enableSessions' - KW_SSL_METHOD = "sslMethod" - KW_SSL_CIPHERS = "sslCiphers" - - def __checkKWArgs(self, kwargs): - for arg, value in ((self.KW_TIMEOUT, False), - (self.KW_ENABLE_SESSIONS, True), - (self.KW_SSL_METHOD, "TLSv1")): - if arg not in kwargs: - kwargs[arg] = value - - def createClientSocket(self, addressTuple, **kwargs): - if not isinstance(addressTuple, (list, tuple)): - return S_ERROR("hostAdress is not in a tuple form ('hostnameorip', port)") - res = gConfig.getOptionsDict("/DIRAC/ConnConf/%s:%s" % addressTuple[0:2]) - if res['OK']: - opts = res['Value'] - for k in opts: - if k not in kwargs: - kwargs[k] = opts[k] - self.__checkKWArgs(kwargs) - result = checkSanity(addressTuple, kwargs) - if not result['OK']: - return result - result = gSocketInfoFactory.getSocket(addressTuple, **kwargs) - if not result['OK']: - return result - socketInfo = result['Value'] - return S_OK(FakeSocket(socketInfo.getSSLSocket(), copies=1)) - - -gSSLSocketFactory = SSLSocketFactory() diff --git a/Core/DISET/private/Transports/GSISSLTransport.py b/Core/DISET/private/Transports/GSISSLTransport.py deleted file mode 100755 index 64f8d09d9ee..00000000000 --- a/Core/DISET/private/Transports/GSISSLTransport.py +++ /dev/null @@ -1,200 +0,0 @@ -__RCSID__ = "$Id$" - -import os -import time -import GSI -from DIRAC.Core.Utilities.LockRing import LockRing -from DIRAC.Core.Utilities.ReturnValues import S_ERROR, S_OK -from DIRAC.Core.DISET.private.Transports.BaseTransport import BaseTransport -from DIRAC.FrameworkSystem.Client.Logger import gLogger -from DIRAC.Core.DISET.private.Transports.SSL.pygsi.SocketInfoFactory import gSocketInfoFactory -from DIRAC.Core.Utilities.Devloader import Devloader - - -GSI.SSL.set_thread_safe() - - -class SSLTransport(BaseTransport): - - __readWriteLock = LockRing().getLock() - - def __init__(self, *args, **kwargs): - self.__writesDone = 0 - self.__locked = False - BaseTransport.__init__(self, *args, **kwargs) - - def __lock(self, timeout=1000): - while self.__locked and timeout: - time.sleep(0.005) - timeout -= 0.005 - if not timeout: - return False - SSLTransport.__readWriteLock.acquire() - if self.__locked: - SSLTransport.__readWriteLock.release() - return self.__lock(timeout) - self.__locked = True - SSLTransport.__readWriteLock.release() - return True - - def __unlock(self): - self.__locked = False - - def setSocketTimeout(self, timeout): - """ - This method is used to chenge the default timeout of the socket - """ - gSocketInfoFactory.setSocketTimeout(timeout) - - def initAsClient(self): - retVal = gSocketInfoFactory.getSocket(self.stServerAddress, **self.extraArgsDict) - if not retVal['OK']: - return retVal - self.oSocketInfo = retVal['Value'] - self.oSocket = self.oSocketInfo.getSSLSocket() - if not self.oSocket.session_reused(): - gLogger.debug("New session connecting to server at %s" % str(self.stServerAddress)) - self.remoteAddress = self.oSocket.getpeername() - return S_OK() - - def initAsServer(self): - if not self.serverMode(): - raise RuntimeError("Must be initialized as server mode") - retVal = gSocketInfoFactory.getListeningSocket(self.stServerAddress, - self.iListenQueueSize, - self.bAllowReuseAddress, - **self.extraArgsDict) - if not retVal['OK']: - return retVal - self.oSocketInfo = retVal['Value'] - self.oSocket = self.oSocketInfo.getSSLSocket() - Devloader().addStuffToClose(self.oSocket) - return S_OK() - - def close(self): - gLogger.debug("Closing socket") - try: - - # Chris 11.09.18 - # I think this will never work, - # From the fsync man page - # """ - # EROFS, EINVAL: fd is bound to a special file (e.g., a pipe, FIFO, or socket) - # which does not support synchronization. - # """" - # For the records, it was added in 67ca305a02621cf36a558cb42896dbb599df9dc9 - # I guess it should be just removed alltogther, but well... - # os.fsync( self.oSocket.fileno() ) - - self.oSocket.close() - except Exception as e: - pass - - def renewServerContext(self): - BaseTransport.renewServerContext(self) - result = gSocketInfoFactory.renewServerContext(self.oSocketInfo) - if not result['OK']: - return result - self.oSocketInfo = result['Value'] - self.oSocket = self.oSocketInfo.getSSLSocket() - return S_OK() - - def handshake(self): - """ - Initiate the client-server handshake and extract credentials - - :return: S_OK (with credentialDict if new session) - """ - retVal = self.oSocketInfo.doServerHandshake() - if not retVal['OK']: - return retVal - creds = retVal['Value'] - if not self.oSocket.session_reused(): - gLogger.debug("New session connecting from client at %s" % str(self.getRemoteAddress())) - for key in creds.keys(): - self.peerCredentials[key] = creds[key] - return S_OK() - - def setClientSocket(self, oSocket): - if self.serverMode(): - raise RuntimeError("Must be initialized as client mode") - self.oSocketInfo.setSSLSocket(oSocket) - self.oSocket = oSocket - self.remoteAddress = self.oSocket.getpeername() - self.oSocket.settimeout(self.oSocketInfo.infoDict['timeout']) - - def acceptConnection(self): - oClientTransport = SSLTransport(self.stServerAddress) - oClientSocket, _stClientAddress = self.oSocket.accept() - retVal = self.oSocketInfo.clone() - if not retVal['OK']: - return retVal - oClientTransport.oSocketInfo = retVal['Value'] - oClientTransport.setClientSocket(oClientSocket) - return S_OK(oClientTransport) - - def _read(self, bufSize=4096, skipReadyCheck=False): - self.__lock() - try: - timeout = self.oSocketInfo.infoDict['timeout'] - if timeout: - start = time.time() - while True: - if timeout: - if time.time() - start > timeout: - return S_ERROR("Socket read timeout exceeded") - try: - return S_OK(self.oSocket.recv(bufSize)) - except GSI.SSL.WantReadError: - time.sleep(0.001) - except GSI.SSL.WantWriteError: - time.sleep(0.001) - except GSI.SSL.ZeroReturnError: - return S_OK("") - except Exception as e: - return S_ERROR("Exception while reading from peer: %s" % str(e)) - finally: - self.__unlock() - - def isLocked(self): - return self.__locked - - def _write(self, buf): - self.__lock() - try: - # Renegotiation - if not self.oSocketInfo.infoDict['clientMode']: - # self.__writesDone += 1 - if self.__writesDone > 1000: - - self.__writesDone = 0 - ok = self.oSocket.renegotiate() - if ok: - try: - ok = self.oSocket.do_handshake() - except Exception as e: - return S_ERROR("Renegotiation failed: %s" % str(e)) - - sentBytes = 0 - timeout = self.oSocketInfo.infoDict['timeout'] - if timeout: - start = time.time() - while sentBytes < len(buf): - try: - if timeout: - if time.time() - start > timeout: - return S_ERROR("Socket write timeout exceeded") - sent = self.oSocket.write(buf[sentBytes:]) - if sent == 0: - return S_ERROR("Connection closed by peer") - if sent > 0: - sentBytes += sent - except GSI.SSL.WantWriteError: - time.sleep(0.001) - except GSI.SSL.WantReadError: - time.sleep(0.001) - except Exception as e: - return S_ERROR("Error while sending: %s" % str(e)) - return S_OK(sentBytes) - finally: - self.__unlock() diff --git a/Core/DISET/private/Transports/SSL/pygsi/SessionManager.py b/Core/DISET/private/Transports/SSL/pygsi/SessionManager.py deleted file mode 100755 index bf0014224d2..00000000000 --- a/Core/DISET/private/Transports/SSL/pygsi/SessionManager.py +++ /dev/null @@ -1,30 +0,0 @@ -# $HeadURL$ -__RCSID__ = "$Id$" - -import GSI - - -class SessionManager: - - def __init__(self): - self.sessionsDict = {} - - def __generateSession(self): - return GSI.SSL.Session() - - def get(self, sessionId): - if sessionId not in self.sessionsDict: - self.sessionsDict[sessionId] = self.__generateSession() - return self.sessionsDict[sessionId] - - def isValid(self, sessionId): - return sessionId in self.sessionsDict and self.sessionsDict[sessionId].valid() - - def free(self, sessionId): - self.sessionsDict[sessionId].free() - - def set(self, sessionId, sessionObject): - self.sessionsDict[sessionId] = sessionObject - - -gSessionManager = SessionManager() diff --git a/Core/DISET/private/Transports/SSL/pygsi/SocketInfo.py b/Core/DISET/private/Transports/SSL/pygsi/SocketInfo.py deleted file mode 100755 index 2626d3c4202..00000000000 --- a/Core/DISET/private/Transports/SSL/pygsi/SocketInfo.py +++ /dev/null @@ -1,361 +0,0 @@ -__RCSID__ = "$Id$" - -import time -import os -import os.path -import GSI -from DIRAC.Core.Utilities.ReturnValues import S_ERROR, S_OK -from DIRAC.Core.Utilities.Network import checkHostsMatch -from DIRAC.Core.Utilities.LockRing import LockRing -from DIRAC.FrameworkSystem.Client.Logger import gLogger -from DIRAC.Core.Security import Locations -from DIRAC.Core.Security.pygsi.X509Chain import X509Chain - -# pylint: disable=line-too-long -DEFAULT_SSL_CIPHERS = "AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:HIGH:MEDIUM:RSA:!3DES:!RC4:!aNULL:!eNULL:!MD5:!SEED:!IDEA" # noqa - -VERIFY_DEPTH = 50 # isn't it A BIT too deep? - - -class SocketInfo: - - __cachedCAsCRLs = False - __cachedCAsCRLsLastLoaded = 0 - __cachedCAsCRLsLoadLock = LockRing().getLock() - - def __init__(self, infoDict, sslContext=None): - self.__retry = 0 - self.infoDict = infoDict - if sslContext: - self.sslContext = sslContext - else: - if self.infoDict['clientMode']: - if 'useCertificates' in self.infoDict and self.infoDict['useCertificates']: - retVal = self.__generateContextWithCerts() - elif 'proxyString' in self.infoDict: - retVal = self.__generateContextWithProxyString() - else: - retVal = self.__generateContextWithProxy() - else: - retVal = self.__generateServerContext() - if not retVal['OK']: - raise Exception(retVal['Message']) - - def __getValue(self, optName, default): - if optName not in self.infoDict: - return default - return self.infoDict[optName] - - def setLocalCredentialsLocation(self, credTuple): - self.infoDict['localCredentialsLocation'] = credTuple - - def getLocalCredentialsLocation(self): - return self.infoDict['localCredentialsLocation'] - - def gatherPeerCredentials(self): - certList = [] - certList = self.sslSocket.get_peer_certificate_chain() - # Servers don't receive the whole chain, the last cert comes alone - if not self.infoDict['clientMode']: - certList.insert(0, self.sslSocket.get_peer_certificate()) - peerChain = X509Chain(certList=certList) - isProxyChain = peerChain.isProxy()['Value'] - isLimitedProxyChain = peerChain.isLimitedProxy()['Value'] - if isProxyChain: - if peerChain.isPUSP()['Value']: - identitySubject = peerChain.getCertInChain( - -2)['Value'].getSubjectNameObject()['Value'] - else: - identitySubject = peerChain.getIssuerCert( - )['Value'].getSubjectNameObject()['Value'] - else: - identitySubject = peerChain.getCertInChain( - 0)['Value'].getSubjectNameObject()['Value'] - credDict = {} - credDict = {'DN': identitySubject.one_line(), - 'CN': identitySubject.commonName, - 'x509Chain': peerChain, - 'isProxy': isProxyChain, - 'isLimitedProxy': isLimitedProxyChain} - diracGroup = peerChain.getDIRACGroup() - if diracGroup['OK'] and diracGroup['Value']: - credDict['group'] = diracGroup['Value'] - self.infoDict['peerCredentials'] = credDict - return credDict - - def setSSLSocket(self, sslSocket): - self.sslSocket = sslSocket - - def getSSLSocket(self): - return self.sslSocket - - def getSSLContext(self): - return self.sslContext - - def clone(self): - try: - return S_OK(SocketInfo(dict(self.infoDict), self.sslContext)) - except Exception as e: - return S_ERROR(str(e)) - - def verifyCallback(self, *args, **kwargs): - # gLogger.debug( "verify Callback %s" % str( args ) ) - if self.infoDict['clientMode']: - return self._clientCallback(*args, **kwargs) - else: - return self._serverCallback(*args, **kwargs) - - def __isSameHost(self, hostCN, hostConn): - """ Guess if it is the same host or not - """ - hostCN_m = hostCN - if '/' in hostCN: - hostCN_m = hostCN.split('/')[1] - if hostCN_m == hostConn: - return True - result = checkHostsMatch(hostCN_m, hostConn) - if not result['OK']: - return False - return result['Value'] - - def _clientCallback(self, conn, cert, errnum, depth, ok): - # This obviously has to be updated - if depth == 0 and ok == 1: - hostnameCN = cert.get_subject().commonName - # if hostnameCN in ( self.infoDict[ 'hostname' ], "host/%s" % self.infoDict[ 'hostname' ] ): - if self.__isSameHost(hostnameCN, self.infoDict['hostname']): - return 1 - else: - gLogger.warn("Server is not who it's supposed to be", - "Connecting to %s and it's %s" % (self.infoDict['hostname'], hostnameCN)) - return ok - return ok - - def _serverCallback(self, conn, cert, errnum, depth, ok): - return ok - - def __getCAStore(self): - SocketInfo.__cachedCAsCRLsLoadLock.acquire() - try: - if not SocketInfo.__cachedCAsCRLs or time.time() - SocketInfo.__cachedCAsCRLsLastLoaded > 900: - # Need to generate the CA Store - casDict = {} - crlsDict = {} - casPath = Locations.getCAsLocation() - if not casPath: - return S_ERROR("No valid CAs location found") - gLogger.debug("CAs location is %s" % casPath) - casFound = 0 - crlsFound = 0 - - SocketInfo.__caStore = GSI.crypto.X509Store() - for fileName in os.listdir(casPath): - filePath = os.path.join(casPath, fileName) - if not os.path.isfile(filePath): - continue - with open(filePath, "rb") as fObj: - pemData = fObj.read() - # Try to load CA Cert - try: - caCert = GSI.crypto.load_certificate(GSI.crypto.FILETYPE_PEM, pemData) - if caCert.has_expired(): - continue - caID = (caCert.get_subject().one_line(), - caCert.get_issuer().one_line()) - caNotAfter = caCert.get_not_after() - if caID not in casDict: - casDict[caID] = (caNotAfter, caCert) - casFound += 1 - else: - if casDict[caID][0] < caNotAfter: - casDict[caID] = (caNotAfter, caCert) - continue - except BaseException: - if fileName.find(".0") == len(fileName) - 2: - gLogger.exception("LOADING %s" % filePath) - if 'IgnoreCRLs' not in self.infoDict or not self.infoDict['IgnoreCRLs']: - # Try to load CRL - try: - crl = GSI.crypto.load_crl(GSI.crypto.FILETYPE_PEM, pemData) - if crl.has_expired(): - continue - crlID = crl.get_issuer().one_line() - crlsDict[crlID] = crl - crlsFound += 1 - continue - except Exception as e: - if fileName.find(".r0") == len(fileName) - 2: - gLogger.exception("LOADING %s ,Exception: %s" % - (filePath, str(e))) - - gLogger.debug("Loaded %s CAs [%s CRLs]" % (casFound, crlsFound)) - SocketInfo.__cachedCAsCRLs = ([casDict[k][1] for k in casDict], - [crlsDict[k] for k in crlsDict]) - SocketInfo.__cachedCAsCRLsLastLoaded = time.time() - except BaseException: - gLogger.exception("Failed to init CA store") - finally: - SocketInfo.__cachedCAsCRLsLoadLock.release() - # Generate CA Store - caStore = GSI.crypto.X509Store() - caList = SocketInfo.__cachedCAsCRLs[0] - for caCert in caList: - caStore.add_cert(caCert) - crlList = SocketInfo.__cachedCAsCRLs[1] - for crl in crlList: - caStore.add_crl(crl) - return S_OK(caStore) - - def __createContext(self): - clientContext = self.__getValue('clientMode', False) - # Initialize context - contextOptions = GSI.SSL.OP_ALL - if clientContext: - methodSuffix = "CLIENT_METHOD" - else: - methodSuffix = "SERVER_METHOD" - contextOptions |= GSI.SSL.OP_NO_SSLv2 | GSI.SSL.OP_NO_SSLv3 - if 'sslMethod' in self.infoDict: - methodName = "%s_%s" % (self.infoDict['sslMethod'], methodSuffix) - else: - methodName = "TLSv1_%s" % (methodSuffix) - try: - method = getattr(GSI.SSL, methodName) - except BaseException: - return S_ERROR("SSL method %s is not valid" % self.infoDict['sslMethod']) - self.sslContext = GSI.SSL.Context(method) - self.sslContext.set_cipher_list( - self.infoDict.get('sslCiphers', DEFAULT_SSL_CIPHERS)) - if contextOptions: - self.sslContext.set_options(contextOptions) - # self.sslContext.set_read_ahead( 1 ) - # Enable GSI? - gsiEnable = False - if not clientContext or self.__getValue('gsiEnable', False): - gsiEnable = True - # DO CA Checks? - if not self.__getValue('skipCACheck', False): - self.sslContext.set_verify(GSI.SSL.VERIFY_PEER | GSI.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, - None, gsiEnable) # Demand a certificate - result = self.__getCAStore() - if not result['OK']: - return result - caStore = result['Value'] - self.sslContext.set_cert_store(caStore) - else: - self.sslContext.set_verify( - GSI.SSL.VERIFY_NONE, None, gsiEnable) # Demand a certificate - return S_OK() - - def __generateContextWithCerts(self): - certKeyTuple = Locations.getHostCertificateAndKeyLocation() - if not certKeyTuple: - return S_ERROR("No valid certificate or key found") - self.setLocalCredentialsLocation(certKeyTuple) - gLogger.debug("Using certificate %s\nUsing key %s" % certKeyTuple) - retVal = self.__createContext() - if not retVal['OK']: - return retVal - # Verify depth to 20 to ensure accepting proxies of proxies of proxies.... - self.sslContext.set_verify_depth(VERIFY_DEPTH) - self.sslContext.use_certificate_chain_file(certKeyTuple[0]) - self.sslContext.use_privatekey_file(certKeyTuple[1]) - return S_OK() - - def __generateContextWithProxy(self): - if 'proxyLocation' in self.infoDict: - proxyPath = self.infoDict['proxyLocation'] - if not os.path.isfile(proxyPath): - return S_ERROR("Defined proxy is not a file") - else: - proxyPath = Locations.getProxyLocation() - if not proxyPath: - return S_ERROR("No valid proxy found") - self.setLocalCredentialsLocation((proxyPath, proxyPath)) - gLogger.debug("Using proxy %s" % proxyPath) - retVal = self.__createContext() - if not retVal['OK']: - return retVal - self.sslContext.use_certificate_chain_file(proxyPath) - self.sslContext.use_privatekey_file(proxyPath) - return S_OK() - - def __generateContextWithProxyString(self): - proxyString = self.infoDict['proxyString'] - self.setLocalCredentialsLocation((proxyString, proxyString)) - gLogger.debug("Using string proxy") - retVal = self.__createContext() - if not retVal['OK']: - return retVal - self.sslContext.use_certificate_chain_string(proxyString) - self.sslContext.use_privatekey_string(proxyString) - return S_OK() - - def __generateServerContext(self): - retVal = self.__generateContextWithCerts() - if not retVal['OK']: - return retVal - self.sslContext.set_session_id("DISETConnection%s" % str(time.time())) - # self.sslContext.get_cert_store().set_flags( GSI.crypto.X509_CRL_CHECK ) - if 'SSLSessionTimeout' in self.infoDict: - timeout = int(self.infoDict['SSLSessionTimeout']) - gLogger.debug("Setting session timeout to %s" % timeout) - self.sslContext.set_session_timeout(timeout) - return S_OK() - - def doClientHandshake(self): - self.sslSocket.set_connect_state() - return self.__sslHandshake() - - def doServerHandshake(self): - self.sslSocket.set_accept_state() - return self.__sslHandshake() - - # @gSynchro - def __sslHandshake(self): - """ - Do the SSL Handshake - - :return: S_ERROR / S_OK with dictionary of user credentials - """ - - start = time.time() - timeout = self.infoDict['timeout'] - while True: - if timeout: - if time.time() - start > timeout: - return S_ERROR("Handshake timeout exceeded") - try: - self.sslSocket.do_handshake() - break - except GSI.SSL.WantReadError: - time.sleep(0.001) - except GSI.SSL.WantWriteError: - time.sleep(0.001) - except GSI.SSL.Error as v: - if self.__retry < 3: - self.__retry += 1 - return self.__sslHandshake() - else: - # gLogger.warn( "Error while handshaking", "\n".join( [ stError[2] for stError in v.args[0] ] ) ) - gLogger.warn("Error while handshaking", v) - return S_ERROR("Error while handshaking") - except Exception as v: - gLogger.warn("Error while handshaking", v) - if self.__retry < 3: - self.__retry += 1 - return self.__sslHandshake() - else: - # gLogger.warn( "Error while handshaking", "\n".join( [ stError[2] for stError in v.args[0] ] ) ) - gLogger.warn("Error while handshaking", v) - return S_ERROR("Error while handshaking") - - credentialsDict = self.gatherPeerCredentials() - if self.infoDict['clientMode']: - hostnameCN = credentialsDict['CN'] - # if hostnameCN.split("/")[-1] != self.infoDict[ 'hostname' ]: - if not self.__isSameHost(hostnameCN, self.infoDict['hostname']): - gLogger.warn("Server is not who it's supposed to be", - "Connecting to %s and it's %s" % (self.infoDict['hostname'], hostnameCN)) - gLogger.debug("", "Authenticated peer (%s)" % credentialsDict['DN']) - return S_OK(credentialsDict) diff --git a/Core/DISET/private/Transports/SSL/pygsi/SocketInfoFactory.py b/Core/DISET/private/Transports/SSL/pygsi/SocketInfoFactory.py deleted file mode 100755 index 9cc8ee44b22..00000000000 --- a/Core/DISET/private/Transports/SSL/pygsi/SocketInfoFactory.py +++ /dev/null @@ -1,188 +0,0 @@ -""" The guy that takes case of managing sockets -""" - -__RCSID__ = "$Id$" - -import socket -import select -import os -import hashlib - -import GSI - -from DIRAC.Core.Utilities.ReturnValues import S_ERROR, S_OK -from DIRAC.Core.Utilities import Network -from DIRAC.Core.DISET.private.Transports.SSL.pygsi.SocketInfo import SocketInfo -from DIRAC.Core.DISET.private.Transports.SSL.pygsi.SessionManager import gSessionManager -from DIRAC.FrameworkSystem.Client.Logger import gLogger - - -class SocketInfoFactory(object): - - def __init__(self): - self.__timeout = 1 - - def setSocketTimeout(self, timeout): - self.__timeout = timeout - - def getSocketTimeout(self): - return self.__timeout - - def generateClientInfo(self, destinationHostname, kwargs): - infoDict = {'clientMode': True, - 'hostname': destinationHostname, - 'timeout': 600, - 'enableSessions': True} - for key in kwargs.keys(): - infoDict[key] = kwargs[key] - try: - return S_OK(SocketInfo(infoDict)) - except Exception as e: - return S_ERROR("Error while creating SSL context: %s" % str(e)) - - def generateServerInfo(self, kwargs): - infoDict = {'clientMode': False, 'timeout': 30} - for key in kwargs.keys(): - infoDict[key] = kwargs[key] - try: - return S_OK(SocketInfo(infoDict)) - except Exception as e: - return S_ERROR(str(e)) - - def __socketConnect(self, hostAddress, timeout, retries=2): - addrs = socket.getaddrinfo(hostAddress[0], hostAddress[1], 0, socket.SOCK_STREAM) - errs = [] - for addr in addrs: - res = self.__sockConnect(addr[4], addr[0], timeout, retries) - if res['OK']: - return res - else: - errs.append(res['Message']) - return S_ERROR(", ".join(errs)) - - def __sockConnect(self, hostAddress, sockType, timeout, retries): - try: - osSocket = socket.socket(sockType, socket.SOCK_STREAM) - except socket.error as e: - gLogger.warn("Exception while creating a socket:", str(e)) - return S_ERROR("Exception while creating a socket:%s" % str(e)) - # osSocket.setblocking( 0 ) - if timeout: - tsocket = self.getSocketTimeout() - gLogger.debug("Connection timeout set to: ", tsocket) - osSocket.settimeout(tsocket) # we try to connect 3 times with 1 second timeout - try: - osSocket.connect(hostAddress) - except socket.error as e: - if e.args[0] == "timed out": - osSocket.close() - if retries: - return self.__sockConnect(hostAddress, sockType, timeout, retries - 1) - else: - return S_ERROR("Can't connect: %s" % str(e)) - if e.args[0] not in (114, 115): - return S_ERROR("Can't connect: %s" % str(e)) - #Connect in progress - oL = select.select([], [osSocket], [], timeout)[1] - if len(oL) == 0: - osSocket.close() - return S_ERROR("Connection timeout") - errno = osSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - if errno != 0: - return S_ERROR("Can't connect: %s" % str((errno, os.strerror(errno)))) - return S_OK(osSocket) - - def __connect(self, socketInfo, hostAddress): - # Connect baby! - result = self.__socketConnect(hostAddress, socketInfo.infoDict['timeout']) - if not result['OK']: - return result - osSocket = result['Value'] - # SSL MAGIC - sslSocket = GSI.SSL.Connection(socketInfo.getSSLContext(), osSocket) - # Generate sessionId - sessionHash = hashlib.md5() - sessionHash.update(str(hostAddress)) - sessionHash.update("|%s" % str(socketInfo.getLocalCredentialsLocation())) - for key in ('proxyLocation', 'proxyString'): - if key in socketInfo.infoDict: - sessionHash.update("|%s" % str(socketInfo.infoDict[key])) - if 'proxyChain' in socketInfo.infoDict: - sessionHash.update("|%s" % socketInfo.infoDict['proxyChain'].dumpAllToString()['Value']) - sessionId = sessionHash.hexdigest() - socketInfo.sslContext.set_session_id(str(hash(sessionId))) - socketInfo.setSSLSocket(sslSocket) - if gSessionManager.isValid(sessionId): - sslSocket.set_session(gSessionManager.get(sessionId)) - # Set the real timeout - if socketInfo.infoDict['timeout']: - sslSocket.settimeout(socketInfo.infoDict['timeout']) - # Connected! - return S_OK(sslSocket) - - def getSocket(self, hostAddress, **kwargs): - hostName = hostAddress[0] - retVal = self.generateClientInfo(hostName, kwargs) - if not retVal['OK']: - return retVal - socketInfo = retVal['Value'] - retVal = Network.getIPsForHostName(hostName) - if not retVal['OK']: - return S_ERROR("Could not resolve %s: %s" % (hostName, retVal['Message'])) - ipList = retVal['Value'] # In that case the first ip always the correct one. - - for _ in xrange(1): # TODO: this retry can be reduced. - connected = False - errorsList = [] - for ip in ipList: - ipAddress = (ip, hostAddress[1]) - retVal = self.__connect(socketInfo, ipAddress) - if retVal['OK']: - sslSocket = retVal['Value'] - connected = True - break - errorsList.append("%s: %s" % (ipAddress, retVal['Message'])) - if not connected: - return S_ERROR("Could not connect to %s: %s" % (hostAddress, "," .join([e for e in errorsList]))) - retVal = socketInfo.doClientHandshake() - if retVal['OK']: - # Everything went ok. Don't need to retry - break - # Did the auth or the connection fail? - if not retVal['OK']: - return retVal - if 'enableSessions' in kwargs and kwargs['enableSessions']: - sessionId = hash(hostAddress) - gSessionManager.set(sessionId, sslSocket.get_session()) - return S_OK(socketInfo) - - def getListeningSocket(self, hostAddress, listeningQueueSize=128, reuseAddress=True, **kwargs): - try: - osSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - except socket.error: - # IPv6 is probably disabled on this node, try IPv4 only instead - osSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if reuseAddress: - osSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - retVal = self.generateServerInfo(kwargs) - if not retVal['OK']: - return retVal - socketInfo = retVal['Value'] - sslSocket = GSI.SSL.Connection(socketInfo.getSSLContext(), osSocket) - sslSocket.bind(hostAddress) - sslSocket.listen(listeningQueueSize) - socketInfo.setSSLSocket(sslSocket) - return S_OK(socketInfo) - - def renewServerContext(self, origSocketInfo): - retVal = self.generateServerInfo(origSocketInfo.infoDict) - if not retVal['OK']: - return retVal - socketInfo = retVal['Value'] - osSocket = origSocketInfo.getSSLSocket().get_socket() - sslSocket = GSI.SSL.Connection(socketInfo.getSSLContext(), osSocket) - socketInfo.setSSLSocket(sslSocket) - return S_OK(socketInfo) - - -gSocketInfoFactory = SocketInfoFactory() diff --git a/Core/DISET/private/Transports/SSL/pygsi/ThreadSafeSSLObject.py b/Core/DISET/private/Transports/SSL/pygsi/ThreadSafeSSLObject.py deleted file mode 100755 index ac61a2a2b6d..00000000000 --- a/Core/DISET/private/Transports/SSL/pygsi/ThreadSafeSSLObject.py +++ /dev/null @@ -1,66 +0,0 @@ -# $HeadURL$ -__RCSID__ = "$Id$" - -import GSI -from DIRAC.Core.Utilities.LockRing import LockRing -from DIRAC.FrameworkSystem.Client.Logger import gLogger - - -class ThreadSafeSSLObject: - cLock = LockRing().getLock() - - def __init__(self, object): - self.cObject = object - - def __getattr__(self, name): - method = getattr(self.cObject, name) - if callable(method): - return _MagicMethod(self.cLock, method, name) - else: - return method - -####################################################################### -# -# Thread-safe hack in order to use multi-threaded applications -# -####################################################################### - - -class _MagicMethod: - # some magic to bind a method to an object - # supports "nested" methods (e.g. examples.getStateName) - def __init__(self, cLock, method, name): - self.sFunctionName = name - self.cMethod = method - self.cLock = cLock - self.iLockDebug = 0 - self.iDebug = 0 - - def lock(self): - if self.iLockDebug: - self.cLock.acquire() - else: - self.cLock.acquire() - - def unlock(self): - if self.iLockDebug: - self.cLock.release() - else: - self.cLock.release() - - def __call__(self, *args): - self.lock() - try: - try: - returnValue = self.cMethod(*args) - except GSI.SSL.ZeroReturnError: - returnValue = 0 - except Exception as v: - if v[0] == -1: - return 0 - else: - gLogger.error("ERROR while executing", "%s( %s ) (%s)" % (self.sFunctionName, str(args)[1:-2], str(v))) - raise v - finally: - self.unlock() - return returnValue diff --git a/Core/DISET/private/Transports/SSL/pygsi/__init__.py b/Core/DISET/private/Transports/SSL/pygsi/__init__.py deleted file mode 100755 index 5156c1e721f..00000000000 --- a/Core/DISET/private/Transports/SSL/pygsi/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# $HeadURL$ -__RCSID__ = "$Id$" diff --git a/Core/DISET/private/Transports/SSLTransport.py b/Core/DISET/private/Transports/SSLTransport.py index 5ab9adc720c..0553ecce0d1 100755 --- a/Core/DISET/private/Transports/SSLTransport.py +++ b/Core/DISET/private/Transports/SSLTransport.py @@ -12,10 +12,7 @@ # Eventhough SSLTransport is not used in this file, it is imported in other module from there, # so do not remove these imports ! -if os.getenv('DIRAC_USE_M2CRYPTO', 'yes').lower() in ('yes', 'true'): - from DIRAC.Core.DISET.private.Transports.M2SSLTransport import SSLTransport -else: - from DIRAC.Core.DISET.private.Transports.GSISSLTransport import SSLTransport +from DIRAC.Core.DISET.private.Transports.M2SSLTransport import SSLTransport def delegate(delegationRequest, kwargs): diff --git a/Core/DISET/private/Transports/test/Test_SSLTransport.py b/Core/DISET/private/Transports/test/Test_SSLTransport.py index 149794ad84c..b306951e5e4 100644 --- a/Core/DISET/private/Transports/test/Test_SSLTransport.py +++ b/Core/DISET/private/Transports/test/Test_SSLTransport.py @@ -13,7 +13,7 @@ from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData from DIRAC.Core.Utilities.CFG import CFG -from DIRAC.Core.DISET.private.Transports import PlainTransport, GSISSLTransport, M2SSLTransport +from DIRAC.Core.DISET.private.Transports import PlainTransport, M2SSLTransport # TODO: Expired hostcert # TODO: Expired usercert @@ -42,13 +42,9 @@ # Transports are now tested in pairs: # "Server-Client" -# This allows for interoperatbility tests between GSI and M2 versions. # Each pair is defined as a string. TRANSPORTTESTS = ("Plain-Plain", - "M2-M2", - "M2-GSI", - "GSI-GSI", - "GSI-M2") + "M2-M2") # https://www.ibm.com/developerworks/linux/library/l-openssl/index.html @@ -131,8 +127,6 @@ def transportByName(transport): return PlainTransport.PlainTransport elif transport.lower() == "m2": return M2SSLTransport.SSLTransport - elif transport.lower() == "gsi": - return GSISSLTransport.SSLTransport raise RuntimeError("Unknown Transport Name: %s" % transport) diff --git a/Core/Security/__init__.py b/Core/Security/__init__.py index 2534815afbd..8e09300a025 100644 --- a/Core/Security/__init__.py +++ b/Core/Security/__init__.py @@ -1,11 +1,10 @@ """ -Just a selector for X509Certificate between PyGSI and M2Crypto based on DIRAC_USE_M2CRYPTO environment variable +Just a selector for X509Certificate """ __RCSID__ = "$Id$" -import os from pkgutil import extend_path @@ -29,10 +28,7 @@ #### -# If we want to use M2Crypto, we add the m2crypto subpackage to the search path +# Add the m2crypto subpackage to the search path # This allows imports like 'from DIRAC.Core.Security.X509Chian...' to work transparently # Nice kind of tricks you find in libraries like xml... -if os.getenv('DIRAC_USE_M2CRYPTO', 'yes').lower() in ('yes', 'true'): - __path__ = extend_path(__path__, __name__ + '.m2crypto') -else: - __path__ = extend_path(__path__, __name__ + '.pygsi') +__path__ = extend_path(__path__, __name__ + '.m2crypto') diff --git a/Core/Security/pygsi/X509CRL.py b/Core/Security/pygsi/X509CRL.py deleted file mode 100644 index 1c88e34d21c..00000000000 --- a/Core/Security/pygsi/X509CRL.py +++ /dev/null @@ -1,130 +0,0 @@ -""" X509CRL is a class for managing X509CRL -This class is used to manage the revoked certificates.... -""" -__RCSID__ = "$Id$" - - -import stat -import os -import tempfile - -from GSI import crypto -from DIRAC import S_OK, S_ERROR -from DIRAC.Core.Utilities import DErrno - - -class X509CRL(object): - - def __init__(self, cert=None): - - self.__pemData = None - - if cert: - self.__loadedCert = True - self.__revokedCert = cert - else: - self.__loadedCert = False - - @classmethod - def instanceFromFile(cls, crlLocation): - """ Instance a X509CRL from a file - """ - chain = cls() - result = chain.loadChainFromFile(crlLocation) - if not result['OK']: - return result - return S_OK(chain) - - def loadChainFromFile(self, crlLocation): - """ - Load a x509CRL certificate from a pem file - Return : S_OK / S_ERROR - """ - try: - fd = open(crlLocation) - pemData = fd.read() - fd.close() - except Exception as e: - return S_ERROR(DErrno.EOF, "%s: %s" % (crlLocation, repr(e).replace(',)', ')'))) - return self.loadChainFromString(pemData) - - def loadChainFromString(self, pemData): - """ - Load a x509CRL certificate from a string containing the pem data - Return : S_OK / S_ERROR - """ - self.__loadedCert = False - try: - self.__revokedCert = crypto.load_crl(crypto.FILETYPE_PEM, pemData) - except Exception as e: - return S_ERROR(DErrno.ECERTREAD, "%s" % repr(e).replace(',)', ')')) - if not self.__revokedCert: - return S_ERROR(DErrno.ECERTREAD) - self.__loadedCert = True - self.__pemData = pemData - - return S_OK() - - def loadProxyFromFile(self, crlLocation): - """ - Load a Proxy from a pem file - Return : S_OK / S_ERROR - """ - try: - fd = open(crlLocation) - pemData = fd.read() - fd.close() - except Exception as e: - return S_ERROR(DErrno.EOF, "%s: %s" % (crlLocation, repr(e).replace(',)', ')'))) - return self.loadProxyFromString(pemData) - - def loadProxyFromString(self, pemData): - """ - Load a Proxy from a pem buffer - Return : S_OK / S_ERROR - """ - return self.loadChainFromString(pemData) - - def dumpAllToString(self): - """ - Dump all to string - """ - if not self.__loadedCert: - return S_ERROR(DErrno.ECERTREAD, "No certificate loaded") - - return S_OK(self.__pemData) - - def dumpAllToFile(self, filename=False): - """ - Dump all to file. If no filename specified a temporal one will be created - """ - retVal = self.dumpAllToString() - if not retVal['OK']: - return retVal - pemData = retVal['Value'] - try: - if not filename: - fd, filename = tempfile.mkstemp() - os.write(fd, pemData) - os.close(fd) - else: - fd = open(filename, "w") - fd.write(pemData) - fd.close() - except Exception as e: - return S_ERROR(DErrno.EWF, "%s: %s" % (filename, repr(e).replace(',)', ')'))) - try: - os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR) - except Exception as e: - return S_ERROR(DErrno.ESPF, "%s: %s" % (filename, repr(e).replace(',)', ')'))) - return S_OK(filename) - - def __str__(self): - repStr = " 0: - name.remove_entry(0) - for entry in raw[0][0][0][1][0][0][0][0]: - name.insert_entry(entry[0][0], entry[0][1]) - data['subject'] = name.one_line() - while name.num_entries() > 0: - name.remove_entry(0) - for entry in raw[0][0][0][2][0][0][0]: - name.insert_entry(entry[0][0], entry[0][1]) - data['issuer'] = name.one_line() - data['notBefore'] = raw[0][0][0][5][0] - data['notAfter'] = raw[0][0][0][5][1] - data['fqan'] = [str(fqan) for fqan in raw[0][0][0][6][0][1][0][1]] - for extBundle in raw[0][0][0][7]: - if extBundle[0] == "VOMS attribute": - attr = GSI.crypto.asn1_loads(str(extBundle[1])).get_value() - attr = attr[0][0][1][0] - try: - data['attribute'] = "%s = %s (%s)" % attr - data['vo'] = attr[2] - except Exception as _ex: - data['attribute'] = "Cannot decode VOMS attribute" - if 'vo' not in data and 'fqan' in data: - data['vo'] = data['fqan'][0].split('/')[1] - return S_OK(data) - return S_ERROR(DErrno.EVOMS, "No VOMS data available") - - def generateProxyRequest(self, bitStrength=1024, limited=False): - """ - Generate a proxy request - Return S_OK( X509Request ) / S_ERROR - """ - if not self.__valid: - return S_ERROR(DErrno.ENOCERT) - - if not limited: - subj = self.__certObj.get_subject() - lastEntry = subj.get_entry(subj.num_entries() - 1) - if lastEntry[0] == 'CN' and lastEntry[1] == "limited proxy": - limited = True - - from DIRAC.Core.Security.pygsi.X509Request import X509Request # pylint: disable=import-error - - req = X509Request() - req.generateProxyRequest(bitStrength=bitStrength, limited=limited) - return S_OK(req) - - def getRemainingSecs(self): - """ - Get remaining lifetime in secs - """ - if not self.__valid: - return S_ERROR(DErrno.ENOCERT) - notAfter = self.__certObj.get_not_after() - remaining = notAfter - Time.dateTime() - return S_OK(max(0, remaining.days * 86400 + remaining.seconds)) - - def getExtensions(self): - """ - Get a decoded list of extensions - """ - if not self.__valid: - return S_ERROR(DErrno.ENOCERT) - extList = [] - for ext in self.__certObj.get_extensions(): - sn = ext.get_sn() - try: - value = ext.get_value() - except Exception: - value = "Cannot decode value" - extList.append((sn, value)) - return S_OK(sorted(extList)) diff --git a/Core/Security/pygsi/X509Chain.py b/Core/Security/pygsi/X509Chain.py deleted file mode 100644 index 4f01f3fd846..00000000000 --- a/Core/Security/pygsi/X509Chain.py +++ /dev/null @@ -1,697 +0,0 @@ -""" X509Chain is a class for managing X509 chains with their Pkeys -""" -__RCSID__ = "$Id$" - -import os -import stat -import tempfile -import hashlib -import random -import binascii - -from GSI import crypto - -from DIRAC import S_OK, S_ERROR -from DIRAC.Core.Utilities import DErrno -from DIRAC.Core.Security.pygsi.X509Certificate import X509Certificate, LIMITED_PROXY_OID -from DIRAC.ConfigurationSystem.Client.Helpers import Registry - -random.seed() - - -class X509Chain(object): - - __pass = None - - def __getPass(self): - return self.__pass - - __validExtensionValueTypes = (basestring,) - - def __init__(self, certList=False, keyObj=False): - self.__isProxy = False - self.__firstProxyStep = 0 - self.__isLimitedProxy = True - self.__isRFC = False - self.__hash = False - if certList: - self.__loadedChain = True - self.__certList = certList - else: - self.__loadedChain = False - if keyObj: - self.__loadedPKey = True - self.__keyObj = keyObj - else: - self.__loadedPKey = False - if self.__loadedChain: - self.__checkProxyness() - - @classmethod - def instanceFromFile(cls, chainLocation): - """ Instance a X509Chain from a file - """ - chain = cls() - result = chain.loadChainFromFile(chainLocation) - if not result['OK']: - return result - return S_OK(chain) - - def loadChainFromFile(self, chainLocation): - """ - Load a x509 chain from a pem file - Return : S_OK / S_ERROR - """ - try: - with open(chainLocation) as fd: - pemData = fd.read() - except IOError as e: - return S_ERROR(DErrno.EOF, "%s: %s" % (chainLocation, repr(e).replace(',)', ')'))) - return self.loadChainFromString(pemData) - - def loadChainFromString(self, data, dataFormat=crypto.FILETYPE_PEM): - """ - Load a x509 cert from a string containing the pem data - Return : S_OK / S_ERROR - """ - self.__loadedChain = False - try: - self.__certList = crypto.load_certificate_chain(crypto.FILETYPE_PEM, data) - except Exception as e: - return S_ERROR(DErrno.ECERTREAD, "%s" % repr(e).replace(',)', ')')) - if not self.__certList: - return S_ERROR(DErrno.EX509) - self.__loadedChain = True - # Update internals - self.__checkProxyness() - return S_OK() - - def setChain(self, certList): - """ - Set the chain - Return : S_OK / S_ERROR - """ - self.__certList = certList - self.__loadedChain = True - return S_OK() - - def loadKeyFromFile(self, chainLocation, password=False): - """ - Load a PKey from a pem file - Return : S_OK / S_ERROR - """ - try: - with open(chainLocation) as fd: - pemData = fd.read() - except Exception as e: - return S_ERROR(DErrno.EOF, "%s: %s" % (chainLocation, repr(e).replace(',)', ')'))) - return self.loadKeyFromString(pemData, password) - - def loadKeyFromString(self, pemData, password=False): - """ - Load a xPKey from a string containing the pem data - Return : S_OK / S_ERROR - """ - self.__loadedPKey = False - try: - self.__keyObj = crypto.load_privatekey(crypto.FILETYPE_PEM, pemData, password) - except Exception as e: - return S_ERROR(DErrno.ECERTREAD, "%s (Probably bad pass phrase?)" % repr(e).replace(',)', ')')) - self.__loadedPKey = True - return S_OK() - - def setPKey(self, pkeyObj): - """ - Set the chain - Return : S_OK / S_ERROR - """ - self.__keyObj = pkeyObj - self.__loadedPKey = True - return S_OK() - - def loadProxyFromFile(self, chainLocation): - """ - Load a Proxy from a pem file - Return : S_OK / S_ERROR - """ - try: - with open(chainLocation) as fd: - pemData = fd.read() - except Exception as e: - return S_ERROR(DErrno.EOF, "%s: %s" % (chainLocation, repr(e).replace(',)', ')'))) - return self.loadProxyFromString(pemData) - - def loadProxyFromString(self, pemData): - """ - Load a Proxy from a pem buffer - Return : S_OK / S_ERROR - """ - retVal = self.loadChainFromString(pemData) - if not retVal['OK']: - return retVal - return self.loadKeyFromString(pemData) - - def __getProxyExtensionList(self, diracGroup=False, rfc=False, rfcLimited=False): - """ - Get the list of extensions for a proxy - """ - extList = [] - extList.append(crypto.X509Extension('keyUsage', - 'critical, digitalSignature, keyEncipherment, dataEncipherment')) - if diracGroup and isinstance(diracGroup, self.__validExtensionValueTypes): - extList.append(crypto.X509Extension('diracGroup', diracGroup)) - if rfc or rfcLimited: - blob = [["1.3.6.1.5.5.7.21.1"]] if not rfcLimited else [[LIMITED_PROXY_OID]] - asn1Obj = crypto.ASN1(blob) - asn1Obj[0][0].convert_to_object() - asn1dump = binascii.hexlify(asn1Obj.dump()) - extval = "critical,DER:" + ":".join(asn1dump[i:i + 2] for i in range(0, len(asn1dump), 2)) - ext = crypto.X509Extension("proxyCertInfo", extval) - extList.append(ext) - return extList - - def getCertInChain(self, certPos=0): - """ - Get a certificate in the chain - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(X509Certificate(self.__certList[certPos])) - - def getIssuerCert(self): - """ - Get a issuer cert in the chain - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if self.__isProxy: - return S_OK(X509Certificate(self.__certList[self.__firstProxyStep + 1])) - - return S_OK(X509Certificate(self.__certList[-1])) - - def getPKeyObj(self): - """ - Get the pkey obj - """ - if not self.__loadedPKey: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(self.__keyObj) - - def getCertList(self): - """ - Get the cert list - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(self.__certList) - - def getNumCertsInChain(self): - """ - Numbers of certificates in chain - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(len(self.__certList)) - - def generateProxyToString(self, lifeTime, diracGroup=False, strength=1024, limited=False, rfc=False, proxyKey=False): - """ - Generate a proxy and get it as a string - - Args: - lifeTime (int): expected lifetime in seconds of proxy - diracGroup (str): diracGroup to add to the certificate - strength (int): length in bits of the pair - limited (bool): Create a limited proxy - - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if not self.__loadedPKey: - return S_ERROR(DErrno.ENOPKEY) - - if self.__isProxy: - rfc = self.isRFC().get('Value', False) - - issuerCert = self.__certList[0] - - if not proxyKey: - proxyKey = crypto.PKey() - proxyKey.generate_key(crypto.TYPE_RSA, strength) - - proxyCert = crypto.X509() - - if rfc: - proxyCert.set_serial_number(str(int(random.random() * 10 ** 10))) - cloneSubject = issuerCert.get_subject().clone() - cloneSubject.insert_entry("CN", str(int(random.random() * 10 ** 10))) - proxyCert.set_subject(cloneSubject) - proxyCert.add_extensions(self.__getProxyExtensionList(diracGroup, rfc and not limited, rfc and limited)) - else: - proxyCert.set_serial_number(issuerCert.get_serial_number()) - cloneSubject = issuerCert.get_subject().clone() - if limited: - cloneSubject.insert_entry("CN", "limited proxy") - else: - cloneSubject.insert_entry("CN", "proxy") - proxyCert.set_subject(cloneSubject) - proxyCert.add_extensions(self.__getProxyExtensionList(diracGroup)) - - proxyCert.set_issuer(issuerCert.get_subject()) - proxyCert.set_version(issuerCert.get_version()) - proxyCert.set_pubkey(proxyKey) - proxyCert.gmtime_adj_notBefore(-900) - proxyCert.gmtime_adj_notAfter(int(lifeTime)) - proxyCert.sign(self.__keyObj, 'sha256') - - proxyString = "%s%s" % (crypto.dump_certificate(crypto.FILETYPE_PEM, proxyCert), - crypto.dump_privatekey(crypto.FILETYPE_PEM, proxyKey)) - for i in range(len(self.__certList)): - proxyString += crypto.dump_certificate(crypto.FILETYPE_PEM, self.__certList[i]) - - return S_OK(proxyString) - - def generateProxyToFile(self, filePath, lifeTime, diracGroup=False, strength=1024, limited=False, rfc=False): - """ - Generate a proxy and put it into a file - - Args: - filePath: file to write - lifeTime: expected lifetime in seconds of proxy - diracGroup: diracGroup to add to the certificate - strength: length in bits of the pair - limited: Create a limited proxy - """ - retVal = self.generateProxyToString(lifeTime, diracGroup, strength, limited, rfc) - if not retVal['OK']: - return retVal - try: - with open(filePath, 'w') as fd: - fd.write(retVal['Value']) - except Exception as e: - return S_ERROR(DErrno.EWF, "%s :%s" % (filePath, repr(e).replace(',)', ')'))) - try: - os.chmod(filePath, stat.S_IRUSR | stat.S_IWUSR) - except Exception as e: - return S_ERROR(DErrno.ESPF, "%s :%s" % (filePath, repr(e).replace(',)', ')'))) - return S_OK() - - def isProxy(self): - """ - Check wether this chain is a proxy - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(self.__isProxy) - - def isLimitedProxy(self): - """ - Check wether this chain is a proxy - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(self.__isProxy and self.__isLimitedProxy) - - def isValidProxy(self, ignoreDefault=False): - """ - Check wether this chain is a valid proxy - checks if its a proxy - checks if its expired - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if not self.__isProxy: - return S_ERROR(DErrno.ENOCHAIN, "Chain is not a proxy") - elif self.hasExpired()['Value']: - return S_ERROR(DErrno.ENOCHAIN) - elif ignoreDefault: - groupRes = self.getDIRACGroup(ignoreDefault=ignoreDefault) - if not groupRes['OK']: - return groupRes - if not groupRes['Value']: - return S_ERROR(DErrno.ENOGROUP) - return S_OK(True) - - def isVOMS(self): - """ - Check wether this chain is a proxy - """ - retVal = self.isProxy() - if not retVal['OK'] or not retVal['Value']: - return retVal - for i in range(len(self.__certList)): - cert = self.getCertInChain(i)['Value'] - if cert.hasVOMSExtensions()['Value']: - return S_OK(True) - return S_OK(False) - - def getVOMSData(self): - """ - Check wether this chain is a proxy - """ - retVal = self.isProxy() - if not retVal['OK'] or not retVal['Value']: - return retVal - for i in range(len(self.__certList)): - cert = self.getCertInChain(i)['Value'] - res = cert.getVOMSData() - if res['OK']: - return res - return S_ERROR(DErrno.EVOMS) - - def __checkProxyness(self): - self.__hash = False - self.__firstProxyStep = len(self.__certList) - 2 # -1 is user cert by default, -2 is first proxy step - self.__isProxy = True - self.__isRFC = None - self.__isLimitedProxy = False - prevDNMatch = 2 - # If less than 2 steps in the chain is no proxy - if len(self.__certList) < 2: - self.__isProxy = False - return - # Check proxyness in steps - for step in range(len(self.__certList) - 1): - issuerMatch = self.__checkIssuer(step, step + 1) - if not issuerMatch: - self.__isProxy = False - return - # Do we need to check the proxy DN? - if prevDNMatch: - dnMatch = self.__checkProxyDN(step, step + 1) - # No DN match - if dnMatch == 0: - # If we are not in the first step we've found the entity cert - if step > 0: - self.__firstProxyStep = step - 1 - # If we are in the first step this is not a proxy - else: - self.__isProxy = False - return - # Limited proxy DN match - elif dnMatch == 2: - self.__isLimitedProxy = True - if prevDNMatch != 2: - self.__isProxy = False - self.__isLimitedProxy = False - return - prevDNMatch = dnMatch - - def __checkProxyDN(self, certStep, issuerStep): - """ - Check the proxy DN in a step in the chain - 0 = no match - 1 = proxy match - 2 = limited proxy match - """ - - issuerSubject = self.__certList[issuerStep].get_subject() - proxySubject = self.__certList[certStep].get_subject().clone() - psEntries = proxySubject.num_entries() - lastEntry = proxySubject.get_entry(psEntries - 1) - limited = False - if lastEntry[0] != 'CN': - return 0 - if lastEntry[1] not in ('proxy', 'limited proxy'): - extList = self.__certList[certStep].get_extensions() - for ext in extList: - if ext.get_sn() == "proxyCertInfo": - contraint = [line.split(":")[1].strip() for line in ext.get_value().split("\n") - if line.split(":")[0] == "Path Length Constraint"] - if not contraint: - return 0 - if self.__isRFC is None: - self.__isRFC = True - if contraint[0] == LIMITED_PROXY_OID: - limited = True - else: - if self.__isRFC is None: - self.__isRFC = False - if lastEntry[1] == "limited proxy": - limited = True - proxySubject.remove_entry(psEntries - 1) - if not issuerSubject.one_line() == proxySubject.one_line(): - return 0 - return 1 if not limited else 2 - - def __checkIssuer(self, certStep, issuerStep): - """ - Check the issuer is really the issuer - """ - issuerCert = self.__certList[issuerStep] - cert = self.__certList[certStep] - return cert.verify_pkey_is_issuer(issuerCert.get_pubkey()) - - def getDIRACGroup(self, ignoreDefault=False): - """ - Get the dirac group if present - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if not self.__isProxy: - return S_ERROR(DErrno.EX509, "Chain does not contain a valid proxy") - if self.isPUSP()['Value']: - return self.getCertInChain(self.__firstProxyStep - 2)['Value'].getDIRACGroup(ignoreDefault=ignoreDefault) - # The code below will find the first match of the DIRAC group - for i in range(len(self.__certList) - 1, -1, -1): - retVal = self.getCertInChain(i)['Value'].getDIRACGroup(ignoreDefault=True) - if retVal['OK'] and 'Value' in retVal and retVal['Value']: - return retVal - # No DIRAC group found, try to get the default one - return self.getCertInChain(self.__firstProxyStep)['Value'].getDIRACGroup(ignoreDefault=ignoreDefault) - - def hasExpired(self): - """ - Is any of the elements in the chain expired? - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - for iC in range(len(self.__certList) - 1, -1, -1): - if self.__certList[iC].has_expired(): - return S_OK(True) - return S_OK(False) - - def getNotAfterDate(self): - """ - Get the smallest not after date - Does not return the smallest limitation - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - notAfter = self.__certList[0].get_not_after() - for iC in range(len(self.__certList) - 1, -1, -1): - stepNotAfter = self.__certList[iC].get_not_after() - if self.__certList[iC].has_expired(): - return S_OK(stepNotAfter) - if notAfter > stepNotAfter: - notAfter = stepNotAfter - return S_OK(notAfter) - - def generateProxyRequest(self, bitStrength=1024, limited=False): - """ - Generate a proxy request - Return S_OK( X509Request ) / S_ERROR - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if not bitStrength: - return S_ERROR(DErrno.EX509, "bitStrength has to be greater than 1024 (%s)" % bitStrength) - x509 = self.getCertInChain(0)['Value'] - return x509.generateProxyRequest(bitStrength, limited) - - def generateChainFromRequestString(self, pemData, lifetime=86400, requireLimited=False, diracGroup=False, rfc=True): - """ - Generate a x509 chain from a request - return S_OK( string ) / S_ERROR - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - if not self.__loadedPKey: - return S_ERROR(DErrno.ENOPKEY) - try: - req = crypto.load_certificate_request(crypto.FILETYPE_PEM, pemData) - except Exception as e: - return S_ERROR(DErrno.ECERTREAD, "Can't load request data: %s" % repr(e).replace(',)', ')')) - limited = requireLimited and self.isLimitedProxy().get('Value', False) - return self.generateProxyToString(lifetime, diracGroup, 1024, limited, rfc, req.get_pubkey()) - - def getRemainingSecs(self): - """ - Get remaining time - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - remainingSecs = self.getCertInChain(0)['Value'].getRemainingSecs()['Value'] - for i in range(1, len(self.__certList)): - stepRS = self.getCertInChain(i)['Value'].getRemainingSecs()['Value'] - remainingSecs = min(remainingSecs, stepRS) - return S_OK(remainingSecs) - - def dumpAllToString(self): - """ - Dump all to string - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - data = crypto.dump_certificate(crypto.FILETYPE_PEM, self.__certList[0]) - if self.__loadedPKey: - data += crypto.dump_privatekey(crypto.FILETYPE_PEM, self.__keyObj) - for i in range(1, len(self.__certList)): - data += crypto.dump_certificate(crypto.FILETYPE_PEM, self.__certList[i]) - return S_OK(data) - - def dumpAllToFile(self, filename=False): - """ - Dump all to file. If no filename specified a temporal one will be created - """ - retVal = self.dumpAllToString() - if not retVal['OK']: - return retVal - pemData = retVal['Value'] - try: - if not filename: - fd, filename = tempfile.mkstemp() - os.write(fd, pemData) - os.close(fd) - else: - with open(filename, "w") as fd: - fd.write(pemData) - except Exception as e: - return S_ERROR(DErrno.EWF, "%s :%s" % (filename, repr(e).replace(',)', ')'))) - try: - os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR) - except Exception as e: - return S_ERROR(DErrno.ESPF, "%s :%s" % (filename, repr(e).replace(',)', ')'))) - return S_OK(filename) - - def isRFC(self): - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(self.__isRFC) - - def dumpChainToString(self): - """ - Dump only cert chain to string - """ - if not self.__loadedChain: - return S_ERROR(DErrno.ENOCHAIN) - data = '' - for i in range(len(self.__certList)): - data += crypto.dump_certificate(crypto.FILETYPE_PEM, self.__certList[i]) - return S_OK(data) - - def dumpPKeyToString(self): - """ - Dump key to string - """ - if not self.__loadedPKey: - return S_ERROR(DErrno.ENOCHAIN) - return S_OK(crypto.dump_privatekey(crypto.FILETYPE_PEM, self.__keyObj)) - - def __str__(self): - repStr = "= delegatedProxy.getNotAfterDate()['Value'] + assert proxyChain.getNotAfterDate()['Value'] >= delegatedProxy.getNotAfterDate()['Value'] diff --git a/Core/Security/test/Test_pygsiToM2Crypto.py b/Core/Security/test/Test_pygsiToM2Crypto.py deleted file mode 100644 index 1df2a1f7707..00000000000 --- a/Core/Security/test/Test_pygsiToM2Crypto.py +++ /dev/null @@ -1,69 +0,0 @@ -""" This contains unit tests to make sure that the migration between PyGSI and M2Crypto is as smooth as possible""" - -import sys -import os - -import importlib - -from pytest import mark, fixture -parametrize = mark.parametrize - - -def deimportModule(modName): - """ utility function to force a reimport of module - - :param modName: name of the module to remove - """ - - for mod in list(sys.modules): - if mod == modName or mod.startswith('%s.' % modName): - sys.modules.pop(mod) - - # from DIRAC.Core.Security.m2crypto.X509Chain import X509Chain, isPUSPdn - # from DIRAC.Core.Security.m2crypto.X509CRL import X509CRL - # from DIRAC.Core.Security.m2crypto.X509Request import X509Request - # - # from DIRAC.Core.Security.m2crypto.X509Certificate import X509Certificate, \ - # DN_MAPPING, DOMAIN_COMPONENT_OID,\ - # LIMITED_PROXY_OID, ORGANIZATIONAL_UNIT_NAME_OID,\ - # VOMS_EXTENSION_OID, VOMS_FQANS_OID, VOMS_GENERIC_ATTRS_OID - - -@fixture(scope='function') -def set_env(): - """ Fixture to clean before and after the DIRAC import as well as the environment - variable DIRAC_USE_M2CRYPTO - """ - # Cleaning module and unsetting env before - deimportModule('DIRAC') - os.environ.pop('DIRAC_USE_M2CRYPTO', None) - - yield - - # Cleaning module and unsetting env after - deimportModule('DIRAC') - os.environ.pop('DIRAC_USE_M2CRYPTO', None) - - -@parametrize('DIRAC_USE_M2CRYPTO', (None, 'NO', 'ANY', 'YES')) -@parametrize('x509Module', ('X509Chain', 'X509Certificate', 'X509Request', 'X509CRL')) -def test_dynamic_import(DIRAC_USE_M2CRYPTO, x509Module, set_env): - """ Given various value of DIRAC_USE_M2CRYPTO and various - class, make sure that the basic import still works. - It basically is a test of the __init__.py in DIRAC.Core.Security - """ - - fullModuleName = 'DIRAC.Core.Security.' + x509Module - - # if DIRAC_USE_M2CRYPTO is None, we test the default case - # where no env variable is set - if DIRAC_USE_M2CRYPTO: - os.environ['DIRAC_USE_M2CRYPTO'] = DIRAC_USE_M2CRYPTO - - expectedSubPackage = 'm2crypto' - if DIRAC_USE_M2CRYPTO in ('ANY', 'NO'): - expectedSubPackage = 'pygsi' - - importlib.import_module(fullModuleName) - - assert expectedSubPackage in sys.modules[fullModuleName].__file__ diff --git a/Core/Security/test/x509TestUtilities.py b/Core/Security/test/x509TestUtilities.py index d6d51059658..cae7586d729 100644 --- a/Core/Security/test/x509TestUtilities.py +++ b/Core/Security/test/x509TestUtilities.py @@ -266,7 +266,7 @@ def getCertOption(cert, optionName): def deimportDIRAC(): """ clean all what has already been imported from DIRAC. - This method is extremely fragile, but hopefuly, we can get ride of all these + This method is extremely fragile, but hopefully, we can get ride of all these messy tests soon, when PyGSI has gone. """ for mod in list(sys.modules): @@ -275,7 +275,7 @@ def deimportDIRAC(): sys.modules.pop(mod) -X509CHAINTYPES = ('M2_X509Chain', 'GSI_X509Chain') +X509CHAINTYPES = ('M2_X509Chain',) # This fixture will return a pyGSI or M2Crypto X509Chain class # https://docs.pytest.org/en/latest/fixture.html#automatic-grouping-of-tests-by-fixture-instances @@ -283,7 +283,7 @@ def deimportDIRAC(): @fixture(scope="function", params=X509CHAINTYPES) def get_X509Chain_class(request): - """ Fixture to return either the pyGSI or M2Crypto X509Certificate class. + """ Fixture to return either the X509Certificate class. It also 'de-import' DIRAC before and after """ # Clean before @@ -291,10 +291,10 @@ def get_X509Chain_class(request): x509Class = request.param - if x509Class == 'GSI_X509Chain': - from DIRAC.Core.Security.pygsi.X509Chain import X509Chain - else: + if x509Class == 'M2_X509Chain': from DIRAC.Core.Security.m2crypto.X509Chain import X509Chain + else: + raise NotImplementedError() yield X509Chain @@ -302,15 +302,15 @@ def get_X509Chain_class(request): deimportDIRAC() -X509REQUESTTYPES = ('M2_X509Request', 'GSI_X509Request') +X509REQUESTTYPES = ('M2_X509Request',) -# This fixture will return a pyGSI or M2Crypto X509Request class +# This fixture will return a X509Request class # https://docs.pytest.org/en/latest/fixture.html#automatic-grouping-of-tests-by-fixture-instances @fixture(scope="function", params=X509REQUESTTYPES) def get_X509Request(request): - """ Fixture to return either the pyGSI or M2Crypto X509Request instance. + """ Fixture to return either the X509Request instance. It also 'de-import' DIRAC before and after """ # Clean before @@ -318,10 +318,10 @@ def get_X509Request(request): x509Class = request.param - if x509Class == 'GSI_X509Request': - from DIRAC.Core.Security.pygsi.X509Request import X509Request - else: + if x509Class == 'M2_X509Request': from DIRAC.Core.Security.m2crypto.X509Request import X509Request + else: + raise NotImplementedError() def _generateX509Request(): """ Instanciate the object @@ -336,7 +336,7 @@ def _generateX509Request(): def get_X509Chain_from_X509Request(x509ReqObj): - """ This returns an X509Chain class from the same "type" (PyGSI/M2Crypto) as the X509Request + """ This returns an X509Chain class from the same "type" as the X509Request object given as param :param x509ReqObj: instance of a X509Request object @@ -345,9 +345,9 @@ def get_X509Chain_from_X509Request(x509ReqObj): """ # In principle, we should deimport Dirac everywhere, but I am not even sure it makes any difference - if 'pygsi' in x509ReqObj.__class__.__module__: - from DIRAC.Core.Security.pygsi.X509Chain import X509Chain - else: + if 'm2crypto' in x509ReqObj.__class__.__module__: from DIRAC.Core.Security.m2crypto.X509Chain import X509Chain + else: + raise NotImplementedError() return X509Chain diff --git a/README.rst b/README.rst index 82cff0d3740..361315fb3dc 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,7 @@ Each PR is a also subject to check for python 3 compatibility. If you are issuing PRs that are devoted to future versions of DIRAC (so, not for patch releases), for each of the python files touched please run (and react to):: - pylint --rcfile=tests/.pylintrc3k --py3k --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=GSI,numpy path/to/file.py + pylint --rcfile=tests/.pylintrc3k --py3k --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=numpy path/to/file.py Testing diff --git a/docs/source/AdministratorGuide/ServerInstallations/environment_variable_configuration.rst b/docs/source/AdministratorGuide/ServerInstallations/environment_variable_configuration.rst index ab3e89112b9..aca0d25297a 100644 --- a/docs/source/AdministratorGuide/ServerInstallations/environment_variable_configuration.rst +++ b/docs/source/AdministratorGuide/ServerInstallations/environment_variable_configuration.rst @@ -30,7 +30,7 @@ DIRAC_USE_JSON_ENCODE DIRAC_USE_M2CRYPTO If anything else than ``true`` or ``yes`` (default) DIRAC will revert back to using pyGSI instead of m2crypto for handling certificates, proxies, etc. - + Unused since v7r2. DIRAC_VOMSES Can be set to point to a folder containing VOMSES information. See :ref:`multi_vo_dirac` diff --git a/docs/source/AdministratorGuide/technologyPreviews.rst b/docs/source/AdministratorGuide/technologyPreviews.rst index 15e7f1bce31..d8a24b0bbe5 100644 --- a/docs/source/AdministratorGuide/technologyPreviews.rst +++ b/docs/source/AdministratorGuide/technologyPreviews.rst @@ -8,22 +8,6 @@ The reason might be that it is not completely mature yet, or that it can be dist They are meant to stay optional for a couple of releases, and then they become the default. This page keeps a list of such technologies. -M2Crypto -======== - -We aim at replacing the home made wrapper of openssl pyGSI with the standard M2Crypto library. It is by default enabled. -You can disable it by setting the environment variable `DIRAC_USE_M2CRYPTO` to `No`. - -Possible issues ---------------- - -M2Crypto (or any standard tool that respects TLS..) will be stricter than PyGSI. So you may need to adapt your environment a bit. Here are a few hints: - -* SAN in your certificates: if you are contacting a machine using its aliases, make sure that all the aliases are in the SubjectAlternativeName (SAN) field of the certificates -* FQDN in the configuration: SAN normally contains only FQDN, so make sure you use the FQDN in the CS as well (e.g. `mymachine.cern.ch` and not `mymachine`) -* ComponentInstaller screwed: like any change you do on your hosts, the ComponentInstaller will duplicate the entry. So if you change the CS to put FQDN, the machine will appear twice. - - .. _jsonSerialization: JSON Serialization diff --git a/docs/source/DeveloperGuide/AddingNewComponents/CheckYourInstallation/index.rst b/docs/source/DeveloperGuide/AddingNewComponents/CheckYourInstallation/index.rst index fcd3a6fd366..d8d6ca73148 100644 --- a/docs/source/DeveloperGuide/AddingNewComponents/CheckYourInstallation/index.rst +++ b/docs/source/DeveloperGuide/AddingNewComponents/CheckYourInstallation/index.rst @@ -23,10 +23,9 @@ that you have created with *virtualenv* as explained in :ref:`editing_code`. .. code-block:: python - In [1]: import GSI - In [2]: import pyparsing - In [3]: import MySQLdb - In [4]: import DIRAC + In [1]: import pyparsing + In [2]: import MySQLdb + In [3]: import DIRAC Were these imports OK? If not, then you should probably hit the "previous" button of this guide, or check the *pip install* log. diff --git a/environment.yml b/environment.yml index 27afb3e2366..e4cff3aadc1 100644 --- a/environment.yml +++ b/environment.yml @@ -24,7 +24,6 @@ dependencies: - psutil >=4.2.0 - pyasn1 >0.4.1 - pyasn1-modules - - pygsi - python-json-logger >=0.1.8 - pytz >=2015.7 - recommonmark @@ -60,5 +59,7 @@ dependencies: - simplejson >=3.8.1 - tornado >=5.0.0,<6.0.0 - typing >=3.6.6 + # Pin OpenSSL to avoid: https://github.com/DIRACGrid/DIRAC/issues/4489 + - openssl <1.1 - pip: - MySQL-python diff --git a/requirements.txt b/requirements.txt index 2c548ca002c..8d5fa15b47c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ # From repo fts3-rest -git+https://github.com/DIRACGrid/pyGSI.git#egg=GSI # From pypi boto3 diff --git a/tests/runPylint.sh b/tests/runPylint.sh index 2de3cea88bd..86f9e0aada9 100755 --- a/tests/runPylint.sh +++ b/tests/runPylint.sh @@ -1,7 +1,7 @@ #!/bin/bash if [[ "${CHECK}" == "pylintPY3K" ]]; then - find . -name "*.py" -and -not -name 'pep8_*' -and -not -path "./WorkloadManagementSystem/PilotAgent/*" -exec pylint -E --rcfile=tests/.pylintrc3k --py3k --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=GSI,numpy {} + + find . -name "*.py" -and -not -name 'pep8_*' -and -not -path "./WorkloadManagementSystem/PilotAgent/*" -exec pylint -E --rcfile=tests/.pylintrc3k --py3k --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=numpy {} + else - find . -name "*.py" -and -not -name 'pep8_*' -exec pylint -E --rcfile=.pylintrc --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=GSI,numpy {} + + find . -name "*.py" -and -not -name 'pep8_*' -exec pylint -E --rcfile=.pylintrc --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" --extension-pkg-whitelist=numpy {} + fi