1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
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
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
119 return expandgangasystemvars(None,value)
120
124
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
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
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
238 if self.inputPW_Widget:
239
240
241
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
259
260 return False
261
262
263 self.buildOpts( self.command.init, False )
264
265 initList = [ self.command.init, self.command.init_parameters[ "pipe" ] ]
266
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
272
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:
281
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
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:
297 currTime = time.time()
298 if currTime - logTimeStamp >= logRepeatDuration:
299 logTimeStamp = currTime
300
301
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
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
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
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
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
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