Package Ganga :: Package GPIDev :: Package Credentials :: Module ICredential
[hide private]
[frames] | no frames]

Source Code for Module Ganga.GPIDev.Credentials.ICredential

  1  ################################################################################
 
  2  # Ganga Project. http://cern.ch/ganga
 
  3  #
 
  4  # $Id: ICredential.py,v 1.6 2009/05/20 13:40:22 moscicki Exp $
 
  5  ################################################################################
 
  6  #
 
  7  # File: ICredential.py
 
  8  # Author: K. Harrison
 
  9  # Created: 060607
 
 10  #
 
 11  # 06/07/2006 KH:  Changed to Ganga.Utility.Shell for shell commands
 
 12  #                 Redefined create and renew methods
 
 13  #
 
 14  # 07/08/2006 KH:  Added isValid() method
 
 15  #
 
 16  # 08/08/2006 KH:  Corrected errors in methods isValid() and timeInSeconds()
 
 17  #                 => thanks to HCL and CLT
 
 18  #                 Make shell an ICredential property
 
 19  #
 
 20  # 06/09/2006 KH:  Changes to allow minimum validity of credential
 
 21  #                 and validity at creation to be specified independently
 
 22  #                 Changes for monitoring component from CLT
 
 23  #
 
 24  # 25/09/2006 KH:  Changed method isValid(), so that default validity is
 
 25  #                 value of self.minValidity
 
 26  #
 
 27  # 10/10/2006 KH:  Made changes to warnings for credential renewal
 
 28  #
 
 29  # 23/11/2006 KH:  Added "pipe" keyword to option dictionary of ICommandSet
 
 30  #                 Added method to determine if credential is available
 
 31  #                 with system/configuration used
 
 32  #                 (requests from CLT)
 
 33  #
 
 34  # 28/02/2007 CLT: Modified GUI-specific and CLI-specific routines in create(),
 
 35  #                 to reflect the use of currentOpts dictionary when building
 
 36  #                 command options
 
 37  #                 GUI-Specific code in create() updated to make use of the new
 
 38  #                 Credentials Manager panel in the GUI
 
 39  #
 
 40  # 27/07/2007 AM:  Updated the renew() method to notify the
 
 41  #                 InternalServices.Coordinator when credentials are
 
 42  #                 detected by the monitoring loop as being invalid
 
 43  #                 so the internal services to be disabled
 
 44  #
 
 45  # 07/12/2007 KH:  Made ICommandSet a component class
 
 46  #
 
 47  # 17/12/2007 KH:  Added function registerCommandSet for registering plugins
 
 48  #                 of category "credential_commands"
 
 49  #
 
 50  # 13/03/2008 KH:  Update for change in configuration system
 
 51  #                 (use "defaults_" instead of "_Properties")
 
 52  #
 
 53  # 18/03/2009 MWS: Added the 'log' option to isValid and shifted the expire
 
 54  #                 message here
 
 55  #
 
 56  # 15/10/2009 MWS: Added possibility to force new check of credentials,
 
 57  #                 rather than relying on cache
 
 58  
 
 59  """Module defining interface class for working with credentials""" 
 60                                                                                  
 
 61  __author__  = "K.Harrison <Harrison@hep.phy.cam.ac.uk>" 
 62  __date__    = "05 November 2009" 
 63  __version__ = "1.11" 
 64  
 
 65  import os, threading, time, tempfile 
 66  
 
 67  from Ganga.GPIDev.Base.Objects import GangaObject 
 68  from Ganga.GPIDev.Schema import ComponentItem, Schema, SimpleItem, Version 
 69  from Ganga.Utility.Config import ConfigError, getConfig, expandgangasystemvars 
 70  from Ganga.Utility.logging import getLogger 
 71  from Ganga.Utility.Plugin.GangaPlugin import allPlugins, PluginManagerError 
 72  from Ganga.Utility.Shell import Shell 
 73  
 
 74  from Ganga.Core.InternalServices import Coordinator 
 75  
 
 76  logger = getLogger() 
 77  logTimeStamp = 0.0 
 78  logRepeatDuration = 120.0 
 79  
 
80 -def registerCommandSet( commandClass = None ):
81 82 try: 83 pluginLoaded = allPlugins.all_dict\ 84 [ "credential_commands" ][ commandClass._name ] 85 except KeyError: 86 allPlugins.add( commandClass, "credential_commands", commandClass._name ) 87 88 return None
89
90 -class ICommandSet( GangaObject ):
91 """ 92 Class used to define shell commands and options for working with credentials 93 """ 94 _schema = Schema( Version( 1, 0 ), { 95 "init" : SimpleItem( defvalue = "", 96 doc = "Command for creating/initialising credential", 97 filter = '__expandGangaSystemVars__'), 98 "info" : SimpleItem( defvalue = "", 99 doc = "Command for obtaining information about credential", 100 filter = '__expandGangaSystemVars__'), 101 "destroy" : SimpleItem( defvalue = "", 102 doc = "Command for destroying credential", 103 filter = '__expandGangaSystemVars__'), 104 "init_parameters" : SimpleItem( defvalue = {}, 105 doc = "Dictionary of parameter-value pairs to pass to init command" ), 106 "destroy_parameters" : SimpleItem( defvalue = {}, 107 doc = \ 108 "Dictionary of parameter-value pairs to pass to destroy command" ), 109 "info_parameters" : SimpleItem( defvalue = {}, 110 doc = "Dictionary mapping from Ganga credential properties to command-line options" ), 111 } ) 112 113 _category = "credential_commands" 114 _name = "ICommandSet" 115 _hidden = 1 116 _enable_config = 1 117
118 - def __expandGangaSystemVars__(self, value):
119 return expandgangasystemvars(None,value)
120
121 - def __init__( self ):
122 super( ICommandSet, self ).__init__() 123 return
124
125 - def setConfigValues( self ):
126 """ 127 Update properties using values from relevant section of config file. 128 """ 129 section = "defaults_%s" % self._name 130 config = getConfig( section ) 131 for attribute in self._schema.datadict.keys(): 132 try: 133 value = config[ attribute ] 134 try: 135 value = eval( value ) 136 except: 137 pass 138 setattr( self, attribute, value ) 139 except ConfigError: 140 pass
141 142 registerCommandSet( ICommandSet ) 143
144 -class ICredential( GangaObject ):
145 """ 146 Interface class for working with credentials 147 """ 148 149 _schema = Schema( Version( 1, 0 ), { 150 "maxTry" : SimpleItem( defvalue = 1, 151 doc = "Number of password attempts allowed when creating credential" ), 152 "minValidity" : SimpleItem( defvalue = "00:15", 153 doc = "Default minimum validity" ), 154 "validityAtCreation" : SimpleItem( defvalue = "24:00", 155 doc = "Default credential validity at creation" ), 156 "command" : ComponentItem( category = "credential_commands", 157 defvalue = "ICommandSet", 158 doc = "Set of commands to be used for credential-related operations" ) 159 } ) 160 161 _category = "credentials" 162 _name = "ICredential" 163 _hidden = 1 164 165 _exportmethods = [ "create", "destroy", "isAvailable", "isValid",\ 166 "location", "renew", "timeleft" ] 167
168 - def __init__( self ):
169 super( ICredential, self ).__init__() 170 self.shell = Shell() 171 self.inputPW_Widget = None 172 return
173
174 - def create( self, validity = "", maxTry = 0, minValidity = "", \ 175 check = False ):
176 """ 177 Create credential. 178 179 Arguments other than self: 180 validity - Validity with which credential should be created, 181 specified as string of format "hh:mm" 182 [ Defaults to value of self.validityAtCreation ] 183 maxTry - Number of password attempts allowed 184 [ Defaults to value of self.maxTry ] 185 minValidity - Minimum validity in case checking of 186 pre-existing credential is performed, 187 specified as strong of format "hh:mm" 188 [ Defaults to value of self.minValidity ] 189 check - Flag to request checking of pre-existing 190 credential; if flag is set to true, then new 191 credential is created only if the validity of 192 any pre-existing credential is less than the 193 value of minValidity 194 [ Default: False ] 195 196 Note: create is the same as renew, except for the default value of check 197 198 Return value: True if credential is created successfully, and False 199 otherwise. 200 """ 201 global logTimeStamp 202 203 dummy = False 204 if not self.command.init: 205 dummy = True 206 if self.command.init_parameters.has_key( "valid" ): 207 if not self.command.init_parameters[ "valid" ]: 208 dummy = True 209 210 if dummy: 211 logger.warning( "Dummy CommandSet used - no credential created" ) 212 return False 213 214 if not maxTry: 215 maxTry = self.maxTry 216 217 if not minValidity: 218 minValidity = self.minValidity 219 220 if not validity: 221 validity = self.validityAtCreation 222 223 validityInSeconds = self.timeInSeconds( validity ) 224 225 if not validityInSeconds: 226 logger.warning( "Problems with requested validity: %s" \ 227 % str( validity ) ) 228 return False 229 if check and self.isValid( minValidity ): 230 return True 231 232 ntry = 0 233 234 while ntry < maxTry: 235 236 ntry = ntry + 1 237 # Test if GUI widget is to be used. 238 if self.inputPW_Widget: 239 # Since self.inputPW_Widget is called, current arguments are 240 # ignored since renew() and create() in GUI mode will not be 241 # called with any arguments. 242 if self.inputPW_Widget.ask( self._proxyObject ): 243 logger.debug( "Proceeding to retrieve password from inputPW_Widget." ) 244 __pw = self.inputPW_Widget.getPassword( self._proxyObject ) 245 if not __pw: 246 logger.warning( "Password/passphrase expected!" ) 247 return False 248 try: 249 tFile = tempfile.NamedTemporaryFile() 250 tFile.write( __pw ) 251 tFile.flush() 252 except: 253 del __pw 254 logger.warning( "Could not create secure temporary file for password!" ) 255 return False 256 del __pw 257 else: 258 # Current credential modification denied for various reasons. 259 # see GangaGUI.customDialogs.ask() method for more details. 260 return False 261 # self.inputPW_Widget.ask() may have modified parameters. 262 # Calling buildOpts() to take them into account. 263 self.buildOpts( self.command.init, False ) 264 # Create initialisation list with the 'pipe' parameter 265 initList = [ self.command.init, self.command.init_parameters[ "pipe" ] ] 266 # Append option value pairs 267 for optName, optVal in self.command.currentOpts.iteritems(): 268 initList.append( "%s %s" % ( optName, optVal ) ) 269 status = self.shell.system( "cat %s|%s" % ( tFile.name, " ".join( initList ) ) ) 270 tFile.close() 271 # self.inputPW_Widget dialog postprocessing. 272 # E.g. disable autorenew mechanism if status != 0. 273 self.inputPW_Widget.renewalStatus( self._proxyObject, status ) 274 if status == 0: 275 logger.info( "%s creation/renewal successful." % self._name ) 276 return True 277 else: 278 logger.warning( "%s creation/renewal failed [%s]." % ( self._name, status ) ) 279 return False 280 else: # Non-GUI credential renewal/creation 281 # Check if renewal is from main process (i.e. by bootstrap or user) 282 if threading.currentThread().getName() == 'MainThread': 283 if self.command.init_parameters.has_key( "valid" ): 284 self.command.currentOpts\ 285 [ self.command.init_parameters[ 'valid' ] ] = validity 286 initList = [ self.command.init ] 287 # Append option value pairs 288 for optName, optVal in self.command.currentOpts.iteritems(): 289 initList.append( "%s %s" % ( optName, optVal ) ) 290 status = self.shell.system( " ".join( initList ) ) 291 if status == 0: 292 logger.info( "%s creation/renewal successful." % self._name ) 293 return True 294 else: 295 logger.warning( "%s creation/renewal failed [%s]." % ( self._name, status ) ) 296 else: # create initiated from worker thread from monitoring component. 297 currTime = time.time() 298 if currTime - logTimeStamp >= logRepeatDuration: 299 logTimeStamp = currTime 300 301 # Check validity but print logging messages this time 302 self.isValid( "", True ) 303 _credentialObject = self._name[ 0 ].lower() + self._name[ 1: ] 304 logger.warning( \ 305 "Renew by typing '%s.renew()' at the prompt." % \ 306 ( _credentialObject ) ) 307 308 309 #notify the Core that the credential is not valid 310 _validity = self.timeInSeconds(self.timeleft()) 311 _minValidity = self.timeInSeconds(minValidity)/2 312 if _validity <= max(120,_minValidity): 313 Coordinator.notifyInvalidCredential(self) 314 315 return True 316 317 logger.warning( "%s creation/renewal attempts exceeded %s tries!" % ( self._name, maxTry ) ) 318 return False
319
320 - def destroy( self, allowed_exit = [ 0 ] ):
321 """ 322 Destroy credential 323 324 Argument other than self: 325 allowed_exit - List of exit codes accepted without error 326 when issuing system command for destroying credential 327 328 Return value: False if command for destroying credential is undefined, 329 or True otherwise 330 """ 331 332 if not self.command.destroy: 333 logger.warning( "Dummy CommandSet used - no credential created" ) 334 return False 335 336 destroyList = [ self.command.destroy ] 337 for optName, optVal in self.command.destroyOpts.iteritems(): 338 destroyList.append( "%s %s" % ( optName, optVal ) ) 339 340 Coordinator.notifyInvalidCredential(self) 341 342 status, output, message = \ 343 self.shell.cmd1( " ".join( destroyList ), allowed_exit ) 344 proxyPath = self.location() 345 if proxyPath: 346 os.remove( proxyPath ) 347 return True
348
349 - def isAvailable( self ):
350 351 """ 352 Check whether credential is available with system/configuration used 353 354 No arguments other than self 355 356 Return value: True if credential is available, false otherwise 357 """ 358 359 logger.warning( "Dummy method used - this always returns True" ) 360 361 return True
362 363
364 - def isValid( self, validity = "", log = False, force_check = False ):
365 366 """ 367 Check validity 368 369 Arguments other than self: 370 validity - Minimum time for which credential should be valid, 371 specified as string of format "hh:mm" 372 [ Defaults to valud of self.minValidity ] 373 374 log - Print logger messages if credential not valid 375 376 force_check - Force credential check, rather than relying on cache 377 378 Return value: True if credential is valid for required time, False 379 otherwise. 380 """ 381 382 valid = True 383 384 if not validity: 385 validity = self.minValidity 386 validityInSeconds = self.timeInSeconds( validity ) 387 timeleft = self.timeleft( force_check = force_check ) 388 389 if not timeleft: 390 valid = False 391 else: 392 timeleftInSeconds = self.timeInSeconds( timeleft ) 393 if timeleftInSeconds <= validityInSeconds: 394 valid = False 395 396 if not valid and log: 397 _tl = self.timeleft( force_check = force_check ) 398 if _tl == "-1" or _tl == "0:00:00": 399 _expiryStatement = "has expired!" 400 else: 401 _expiryStatement = "will expire in %s!" % _tl 402 403 itemList = [] 404 text = self._name[ 0 ] 405 for i in range( len( self._name ) - 1 ): 406 character = self._name[ i + 1 ] 407 if character.isupper(): 408 itemList.append( text ) 409 text = character.lower() 410 else: 411 text = "".join( [ text, character ] ) 412 itemList.append( text ) 413 _credentialName = " ".join( itemList ) 414 415 logger.warning( "%s %s" % \ 416 ( _credentialName, _expiryStatement ) ) 417 418 return valid
419
420 - def location( self ):
421 """ 422 Determine credential location 423 424 No arguments other than self 425 426 Return value: Path to credential if found, or empty string otherwise 427 """ 428 429 return ""
430
431 - def renew( self, validity = "", maxTry = 0, minValidity = "", \ 432 check = True ):
433 """ 434 Renew credential. 435 436 Arguments other than self: 437 validity - Validity with which credential should be created, 438 specified as string of format "hh:mm" 439 [ Defaults to value of self.validityAtCreation ] 440 maxTry - Number of password attempts allowed 441 [ Defaults to value of self.maxTry ] 442 minValidity - Minimum validity in case checking of 443 pre-existing credential is performed, 444 specified as strong of format "hh:mm" 445 [ Defaults to value of self.minValidity ] 446 check - Flag to request checking of pre-existing 447 credential; if flag is set to true, then new 448 credential is created only if the validity of 449 any pre-existing credential is less than the 450 value of minValidity 451 [ Default: True ] 452 453 Note: renew is the same as create, except for the default value of check 454 455 Return value: True if new credential is created successfully, and False 456 otherwise. 457 """ 458 status = self.create( validity, maxTry, minValidity, check ) 459 460 return status
461
462 - def timeInSeconds( self, timeString = "" ):
463 """ 464 Convert time string to time in seconds 465 466 Arguments other than self: 467 timeString - Time specified as string of format "hh:mm:ss" 468 469 Return value: Time in seconds (integer) 470 """ 471 472 totalTime = 0 473 timeList = timeString.split( ":" ) 474 if len( timeList ) >= 1: 475 totalTime = totalTime + int( timeList[ 0 ] ) * 60 * 60 476 if len( timeList ) >= 2: 477 totalTime = totalTime + int( timeList[ 1 ] ) * 60 478 if len( timeList ) >= 3: 479 totalTime = totalTime + int (timeList[ 2 ] ) 480 481 return totalTime
482
483 - def timeleft( self, units = "hh:mm:ss", force_check = False ):
484 """ 485 Check time for which credential is valid. 486 487 Arguments other than self: 488 units - String specifying units in which time is returned 489 490 force_check - Force credential check, rather than relying on cache 491 492 Allowed values for units are: 493 "hours" - time returned as in hours 494 "minutes" - time returned in minutes 495 "seconds" - time returned in seconds 496 "hh:mm:ss" [default] - time returned as hours, minutes seconds 497 498 499 Return value: Credential validity as string giving time in requested 500 units, or empty string if command for querying credential validity 501 is unavailable 502 """ 503 504 timeRemaining = self.timeleftInHMS( force_check = force_check ) 505 if timeRemaining not in [ "", "-1" ]: 506 if units in [ "hours", "minutes", "seconds" ]: 507 timeleftInSeconds = self.timeInSeconds( timeRemaining ) 508 if "seconds" == units: 509 timeRemaining = "%.2f" % ( timeleftInSeconds ) 510 elif "minutes" == units: 511 timeRemaining = "%.2f" % ( timeleftInSeconds / 60. ) 512 elif "hours" == units: 513 timeRemaining = "%.2f" % ( timeleftInSeconds / ( 60. * 60. ) ) 514 515 return timeRemaining
516
517 - def timeleftInHMS( self, force_check = False ):
518 """ 519 Determine remaining validity of credential in hours, minutes and seconds 520 521 Argument other than self: 522 force_check - Force credential check, rather than relying on cache 523 524 Return value: String giving credential validity, or empty string 525 if command for querying credential validity is unavailable 526 """ 527 logger.warning( "Dummy method used - no information returned" ) 528 return ""
529