Skip to content

Commit

Permalink
RFC proxies on
Browse files Browse the repository at this point in the history
  • Loading branch information
acasajus committed Jan 20, 2015
1 parent 23b76ee commit beabfdf
Show file tree
Hide file tree
Showing 7 changed files with 837 additions and 29 deletions.
71 changes: 55 additions & 16 deletions ConfigurationSystem/Client/Helpers/CSGlobals.py
Expand Up @@ -8,7 +8,56 @@
"""
__RCSID__ = "$Id$"

#from DIRAC import gConfig
import imp
from DIRAC.Core.Utilities.DIRACSingleton import DIRACSingleton

class Extensions( object ):
__metaclass__ = DIRACSingleton

def __init__( self ):
self.__modules = {}
self.__orderedExtNames = []
self.__csExt = False

def __load( self ):
if self.__orderedExtNames:
return
for extName in self.getCSExtensions() + [ '' ]:
try:
if not extName.endswith( "DIRAC" ):
extension = '%sDIRAC' % extName
res = imp.find_module( extension )
if res[0]:
res[0].close()
self.__orderedExtNames.append( extension )
self.__modules[ extension ] = res
except ImportError:
pass

def getCSExtensions( self ):
if not self.__csExt:
from DIRAC import gConfig
exts = gConfig.getValue( '/DIRAC/Extensions', [] )
for iP in range( len( exts ) ):
ext = exts[ iP ]
if ext.find( "DIRAC" ) == len( ext ) - 5:
ext = ext[:5]
exts[ iP ] = ext
self.__csExt = exts
return self.__csExt

def getInstalledExtensions( self ):
self.__load()
return list( self.__orderedExtNames )

def getExtensionPath( self, extName ):
self.__load()
return self.__modules[ extName ][1]

def getExtensionData( self, extName ):
self.__load()
return self.__modules[ extName ]


def getSetup():
from DIRAC import gConfig
Expand All @@ -26,26 +75,13 @@ def getCSExtensions():
Return list of extensions registered in the CS
They do not include DIRAC
"""
from DIRAC import gConfig
return gConfig.getValue( '/DIRAC/Extensions', [] )
return Extensions().getCSExtensions()

def getInstalledExtensions():
"""
Return list of extensions registered in the CS and available in local installation
"""
import imp
extensions = []
for extension in getCSExtensions():
try:
if not "DIRAC" in extension:
extension = '%sDIRAC' % extension
imp.find_module( extension )
extensions.append( extension )
except ImportError:
pass
extensions.append( 'DIRAC' )
return extensions

return Extensions().getInstalledExtensions()

def skipCACheck():
from DIRAC import gConfig
Expand All @@ -54,3 +90,6 @@ def skipCACheck():
def useServerCertificate():
from DIRAC import gConfig
return gConfig.getValue( "/DIRAC/Security/UseServerCertificate", "false" ).lower() in ( "y", "yes", "true" )



50 changes: 42 additions & 8 deletions ConfigurationSystem/Client/LocalConfiguration.py
Expand Up @@ -13,6 +13,7 @@
from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
from DIRAC.ConfigurationSystem.private.Refresher import gRefresher
from DIRAC.ConfigurationSystem.Client.PathFinder import getServiceSection, getAgentSection, getExecutorSection
from DIRAC.Core.Utilities.Devloader import Devloader

class LocalConfiguration:
"""
Expand Down Expand Up @@ -93,6 +94,12 @@ def __registerBasicOptions( self ):
self.__setUseCertByCmd )
self.registerCmdOpt( "d", "debug", "Set debug mode (-ddd is extra debug)",
self.__setDebugMode )
devLoader = Devloader()
if devLoader.enabled:
self.registerCmdOpt( "", "autoreload", "Automatically restart if there's any change in the module",
self.__setAutoreload )
self.registerCmdOpt( "", "license", "Show DIRAC's LICENSE",
self.showLicense )
self.registerCmdOpt( "h", "help", "Shows this help",
self.showHelp )

Expand Down Expand Up @@ -228,12 +235,12 @@ def __parseCommandLine( self ):
shortOption += "%s" % optionTuple[0]
else:
if optionTuple[0]:
gLogger.error( "Short option has been already defined", "-%s" % optionTuple[0] )
gLogger.error( "Short option -%s has been already defined" % optionTuple[0] )
if not optionTuple[1] in longOptionList:
longOptionList.append( "%s" % optionTuple[1] )
else:
if optionTuple[1]:
gLogger.error( "Long option has been already defined", "--%s" % optionTuple[1] )
gLogger.error( "Long option --%s has been already defined" % optionTuple[1] )

try:
opts, args = getopt.gnu_getopt( sys.argv[1:], shortOption, longOptionList )
Expand All @@ -243,6 +250,11 @@ def __parseCommandLine( self ):
gLogger.fatal( "Error when parsing command line arguments: %s" % str( x ) )
self.showHelp( exitCode = 2 )

for o, v in opts:
if o in ( '-h', '--help' ):
self.showHelp()
sys.exit(2)

self.cliAdditionalCFGFiles = [ arg for arg in args if arg[-4:] == ".cfg" ]
self.commandArgList = [ arg for arg in args if not arg[-4:] == ".cfg" ]
self.parsedOptionList = opts
Expand Down Expand Up @@ -308,7 +320,7 @@ def __addUserDataToConfiguration( self ):
self.unprocessedSwitches = []

for optionName, optionValue in self.parsedOptionList:
optionName = optionName.replace( "-", "" )
optionName = optionName.lstrip( "-" )
for definedOptionTuple in self.commandOptionList:
if optionName == definedOptionTuple[0].replace( ":", "" ) or \
optionName == definedOptionTuple[1].replace( "=", "" ):
Expand Down Expand Up @@ -347,7 +359,7 @@ def isCSEnabled( self ):
def syncRemoteConfiguration( self, strict = False ):
"""
Force a Resync with Configuration Server
Under normal conditions this is triggered by an access to any
Under normal conditions this is triggered by an access to any
configuration data.
"""
if self.componentName == "Configuration/Server" :
Expand Down Expand Up @@ -426,9 +438,31 @@ def __setDebugMode( self, dummy = False ):
self.__debugMode += 1
return S_OK()

def __setAutoreload( self, filepath = False ):
devLoader = Devloader()
devLoader.bootstrap()
if filepath:
devLoader.watchFile( filepath )
gLogger.notice( "Devloader started" )
return S_OK()

def getDebugMode( self ):
return self.__debugMode

def showLicense( self, dummy = False ):
"""
Print license
"""
lpath = os.path.join( DIRAC.rootPath, "DIRAC", "LICENSE" )
sys.stdout.write( " - DIRAC is GPLv3 licensed\n\n" )
try:
with open( lpath ) as fd:
sys.stdout.write( fd.read() )
except IOError:
sys.stdout.write( "Can't find GPLv3 license at %s. Somebody stole it!\n" % lpath )
sys.stdout.write( "Please check out http://www.gnu.org/licenses/gpl-3.0.html for more info\n" )
DIRAC.exit(0)

def showHelp( self, dummy = False, exitCode = 0 ):
"""
Printout help message including a Usage message if defined via setUsageMessage method
Expand Down Expand Up @@ -461,12 +495,12 @@ def showHelp( self, dummy = False, exitCode = 0 ):
for iPos in range( iLastOpt + 1, len( self.commandOptionList ) ):
optionTuple = self.commandOptionList[ iPos ]
if optionTuple[0].endswith( ':' ):
line = " -%s --%s : %s" % ( optionTuple[0][:-1].ljust( 2 ),
(optionTuple[1][:-1] + ' <value> ').ljust( 22 ),
optionTuple[2] )
line = "\n -%s --%s : %s" % ( optionTuple[0][:-1].ljust( 2 ),
(optionTuple[1][:-1] + ' <value> ').ljust( 22 ),
optionTuple[2] )
gLogger.notice( line )
else:
gLogger.notice( " -%s --%s : %s" % ( optionTuple[0].ljust( 2 ), optionTuple[1].ljust( 22 ), optionTuple[2] ) )
gLogger.notice( "\n -%s --%s : %s" % ( optionTuple[0].ljust( 2 ), optionTuple[1].ljust( 22 ), optionTuple[2] ) )

gLogger.notice( "" )
DIRAC.exit( exitCode )
Expand Down
2 changes: 2 additions & 0 deletions Core/DISET/private/Transports/SSLTransport.py
Expand Up @@ -10,6 +10,7 @@
from DIRAC.Core.DISET.private.Transports.BaseTransport import BaseTransport
from DIRAC.FrameworkSystem.Client.Logger import gLogger
from DIRAC.Core.DISET.private.Transports.SSL.SocketInfoFactory import gSocketInfoFactory
from DIRAC.Core.Utilities.Devloader import Devloader
from DIRAC.Core.Security import Locations
from DIRAC.Core.Security.X509Chain import X509Chain
from DIRAC.Core.Security.X509Certificate import X509Certificate
Expand Down Expand Up @@ -64,6 +65,7 @@ def initAsServer( self ):
return retVal
self.oSocketInfo = retVal[ 'Value' ]
self.oSocket = self.oSocketInfo.getSSLSocket()
Devloader().addStuffToClose( self.oSocket )
return S_OK()

def close( self ):
Expand Down
2 changes: 1 addition & 1 deletion Core/Security/ProxyInfo.py
Expand Up @@ -86,7 +86,7 @@ def formatProxyInfoAsString( infoDict ):
leftAlign = 13
contentList = []
for field in ( 'subject', 'issuer', 'identity', ( 'secondsLeft', 'timeleft' ),
( 'group', 'DIRAC group' ), 'path', 'username', ( 'groupProperties', "properties" ),
( 'group', 'DIRAC group' ), 'rfc', 'path', 'username', ( 'groupProperties', "properties" ),
( 'hasVOMS', 'VOMS' ), ( 'VOMS', 'VOMS fqan' ), ( 'VOMSError', 'VOMS Error' ) ):
if type( field ) == types.StringType:
dispField = field
Expand Down
29 changes: 25 additions & 4 deletions Core/Security/X509Chain.py
Expand Up @@ -27,6 +27,7 @@ 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
Expand Down Expand Up @@ -341,6 +342,7 @@ 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
Expand Down Expand Up @@ -385,14 +387,29 @@ def __checkProxyDN( self, certStep, issuerStep ):
proxySubject = self.__certList[ certStep ].get_subject().clone()
psEntries = proxySubject.num_entries()
lastEntry = proxySubject.get_entry( psEntries - 1 )
if lastEntry[0] != 'CN' or lastEntry[1] not in ( 'proxy', 'limited proxy' ):
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 len( contraint ) == 0:
return 0
if self.__isRFC == None:
self.__isRFC = True
if contraint[0] == "1.3.6.1.4.1.3536.1.1.1.9":
limited = True
else:
if self.__isRFC == 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
if lastEntry[1] == "limited proxy":
return 2
return 1
return 1 if not limited else 2

def __checkIssuer( self, certStep, issuerStep ):
"""
Expand Down Expand Up @@ -556,6 +573,9 @@ def dumpAllToFile( self, filename = False ):
return S_ERROR( "Cannot set permissions to file %s :%s" % ( filename, str( e ) ) )
return S_OK( filename )

def isRFC( self ):
return self.__isRFC

def dumpChainToString( self ):
"""
Dump only cert chain to string
Expand Down Expand Up @@ -601,6 +621,7 @@ def getCredentials( self, ignoreDefault = False ):
'validGroup' : False }
if self.__isProxy:
credDict[ 'identity'] = self.__certList[ self.__firstProxyStep + 1 ].get_subject().one_line()
credDict[ 'rfc' ] = self.__isRFC
retVal = Registry.getUsernameForDN( credDict[ 'identity' ] )
if not retVal[ 'OK' ]:
return S_OK( credDict )
Expand Down
91 changes: 91 additions & 0 deletions Core/Utilities/Devloader.py
@@ -0,0 +1,91 @@

import sys
import os
import types
import threading
import time

from DIRAC import gLogger
from DIRAC.Core.Utilities.DIRACSingleton import DIRACSingleton
from DIRAC.ConfigurationSystem.Client.Helpers.CSGlobals import Extensions

class Devloader( object ):
__metaclass__ = DIRACSingleton

def __init__( self ):
self.__log = gLogger.getSubLogger( "Devloader" )
self.__observers = []
self.__exts = Extensions()
self.__reloaded = False
self.__enabled = True
self.__reloadTask = False
self.__stuffToClose = []
self.__watchedFiles = []
self.__modifyTimes = {}

def addStuffToClose( self, stuff ):
self.__stuffToClose.append( stuff )

@property
def enabled( self ):
return self.__enabled

def watchFile( self, fp ):
if os.path.isfile( fd ):
self.__watchedFiles.append( fd )
return True
return False

def __restart( self ):
self.__reloaded = True

for stuff in self.__stuffToClose:
try:
self.__log.always( "Closing %s" % stuff )
sys.stdout.flush()
stuff.close()
except:
gLogger.exception( "Could not close %s" % stuff )

python = sys.executable
os.execl(python, python, * sys.argv)

def bootstrap( self ):
if not self.__enabled:
return False
if self.__reloadTask:
return True

self.__reloadTask = threading.Thread( target = self.__reloadOnUpdate )
self.__reloadTask.setDaemon(1)
self.__reloadTask.start()

def __reloadOnUpdate( self ):
while True:
time.sleep(1)
if self.__reloaded:
return
for modName in sys.modules:
modObj = sys.modules[ modName ]
if not isinstance( modObj, types.ModuleType ):
continue
path = getattr( modObj, "__file__", None )
if not path:
continue
if path.endswith( ".pyc" ) or path.endswith( ".pyo" ):
path = path[:-1]
self.__checkFile( path )
for path in self.__watchedFiles:
self.__checkFile( path )

def __checkFile( self, path ):
try:
modified = os.stat(path).st_mtime
except Exception:
return
if path not in self.__modifyTimes:
self.__modifyTimes[path] = modified
return
if self.__modifyTimes[path] != modified:
self.__log.always( "File system changed (%s). Restarting..." % ( path ) )
self.__restart()

0 comments on commit beabfdf

Please sign in to comment.