Package flumotion :: Package component :: Package bouncers :: Module component
[hide private]

Source Code for Module flumotion.component.bouncers.component

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """ 
 23  Base class and implementation for bouncer components, who perform 
 24  authentication services for other components. 
 25   
 26  Bouncers receive keycards, defined in L{flumotion.common.keycards}, and 
 27  then authenticate them. 
 28   
 29  Passing a keycard over a PB connection will copy all of the keycard's 
 30  attributes to a remote side, so that bouncer authentication can be 
 31  coupled with PB. Bouncer implementations have to make sure that they 
 32  never store sensitive data as an attribute on a keycard. 
 33   
 34  Keycards have three states: REQUESTING, AUTHENTICATED, and REFUSED. When 
 35  a keycard is first passed to a bouncer, it has the state REQUESTING. 
 36  Bouncers should never read the 'state' attribute on a keycard for any 
 37  authentication-related purpose, since it comes from the remote side. 
 38  Typically, a bouncer will only set the 'state' attribute to 
 39  AUTHENTICATED or REFUSED once it has the information to make such a 
 40  decision. 
 41   
 42  Authentication of keycards is performed in the authenticate() method, 
 43  which takes a keycard as an argument. The Bouncer base class' 
 44  implementation of this method will perform some common checks (e.g., is 
 45  the bouncer enabled, is the keycard of the correct type), and then 
 46  dispatch to the do_authenticate method, which is expected to be 
 47  overridden by subclasses. 
 48   
 49  Implementations of do_authenticate should eventually return a keycard 
 50  with the state AUTHENTICATED or REFUSED. It is acceptable for this 
 51  method to return either a keycard or a deferred that will eventually 
 52  return a keycard. 
 53   
 54  FIXME: Currently, a return value of 'None' is treated as rejecting the 
 55  keycard. This is unintuitive. 
 56   
 57  Challenge-response authentication may be implemented in 
 58  do_authenticate(), by returning a keycard still in the state REQUESTING 
 59  but with extra attributes annotating the keycard. The remote side would 
 60  then be expected to set a response on the card, resubmit, at which point 
 61  authentication could be performed. The exact protocol for this depends 
 62  on the particular keycard class and set of bouncers that can 
 63  authenticate that keycard class. 
 64   
 65  It is expected that a bouncer implementation keeps references on the 
 66  currently active set of authenticated keycards. These keycards can then 
 67  be revoked at any time by the bouncer, which will be effected through an 
 68  'expireKeycard' call. When the code that requested the keycard detects 
 69  that the keycard is no longer necessary, it should notify the bouncer 
 70  via calling 'removeKeycardId'. 
 71   
 72  The above process is leak-prone, however; if for whatever reason, the 
 73  remote side is unable to remove the keycard, the keycard will never be 
 74  removed from the bouncer's state. For that reason there is a more robust 
 75  method: if the keycard has a 'ttl' attribute, then it will be expired 
 76  automatically after 'keycard.ttl' seconds have passed. The remote side 
 77  is then responsible for periodically telling the bouncer which keycards 
 78  are still valid via the 'keepAlive' call, which resets the TTL on the 
 79  given set of keycards. 
 80   
 81  Note that with automatic expiry via the TTL attribute, it is still 
 82  preferred, albeit not strictly necessary, that callers of authenticate() 
 83  call removeKeycardId when the keycard is no longer used. 
 84  """ 
 85   
 86  import random 
 87  import time 
 88   
 89  from twisted.internet import defer, reactor 
 90   
 91  from flumotion.common import interfaces, keycards, errors, python 
 92  from flumotion.common.poller import Poller 
 93  from flumotion.common.componentui import WorkerComponentUIState 
 94   
 95  from flumotion.component import component 
 96  from flumotion.twisted import flavors, credentials 
 97   
 98  __all__ = ['Bouncer'] 
 99  __version__ = "$Rev$" 
100   
101  EXPIRE_BLOCK_SIZE = 100 
102   
103   
104 -class BouncerMedium(component.BaseComponentMedium):
105 106 logCategory = 'bouncermedium' 107
108 - def remote_authenticate(self, keycard):
109 """ 110 Authenticates the given keycard. 111 112 @type keycard: L{flumotion.common.keycards.Keycard} 113 """ 114 return self.comp.authenticate(keycard)
115
116 - def remote_keepAlive(self, issuerName, ttl):
117 """ 118 Resets the expiry timeout for keycards issued by issuerName. 119 120 @param issuerName: the issuer for which keycards should be kept 121 alive; that is to say, keycards with the 122 attribute 'issuerName' set to this value will 123 have their ttl values reset. 124 @type issuerName: str 125 @param ttl: the new expiry timeout 126 @type ttl: number 127 """ 128 return self.comp.keepAlive(issuerName, ttl)
129
130 - def remote_removeKeycardId(self, keycardId):
131 try: 132 self.comp.removeKeycardId(keycardId) 133 # FIXME: at least have an exception name please 134 except KeyError: 135 self.warning('Could not remove keycard id %s' % keycardId)
136
137 - def remote_expireKeycardId(self, keycardId):
138 """ 139 Called by bouncer views to expire keycards. 140 """ 141 return self.comp.expireKeycardId(keycardId)
142
143 - def remote_expireKeycardIds(self, keycardIds):
144 """ 145 Called by bouncer views to expire multiple keycards. 146 """ 147 return self.comp.expireKeycardIds(keycardIds)
148
149 - def remote_setEnabled(self, enabled):
150 return self.comp.setEnabled(enabled)
151
152 - def remote_getEnabled(self):
153 return self.comp.getEnabled()
154 155
156 -class Bouncer(component.BaseComponent):
157 """ 158 I am the base class for all bouncer components. 159 160 @cvar keycardClasses: tuple of all classes of keycards this bouncer can 161 authenticate, in order of preference 162 @type keycardClasses: tuple of L{flumotion.common.keycards.Keycard} 163 class objects 164 """ 165 keycardClasses = () 166 componentMediumClass = BouncerMedium 167 logCategory = 'bouncer' 168 169 KEYCARD_EXPIRE_INTERVAL = 2 * 60 # expire every 2 minutes 170
171 - def init(self):
172 self._idCounter = 0 173 self._idFormat = time.strftime('%Y%m%d%H%M%S-%%d') 174 self._keycards = {} # keycard id -> Keycard 175 176 self._expirer = Poller(self._expire, 177 self.KEYCARD_EXPIRE_INTERVAL, 178 start=False) 179 self.enabled = True
180
181 - def setDomain(self, name):
182 self.domain = name
183
184 - def getDomain(self):
185 return self.domain
186
187 - def typeAllowed(self, keycard):
188 """ 189 Verify if the keycard is an instance of a Keycard class specified 190 in the bouncer's keycardClasses variable. 191 """ 192 return isinstance(keycard, self.keycardClasses)
193
194 - def setEnabled(self, enabled):
195 196 def callAndPassthru(result, method, *args): 197 method(*args) 198 return result
199 200 if not enabled and self.enabled: 201 # If we were enabled and are being set to disabled, eject the warp 202 # core^w^w^w^wexpire all existing keycards 203 self.enabled = False 204 self._expirer.stop() 205 d = self.expireAllKeycards() 206 d.addCallback(callAndPassthru, self.on_disabled) 207 return d 208 self.enabled = enabled 209 d = defer.succeed(0) 210 d.addCallback(callAndPassthru, self.on_enabled) 211 return d
212
213 - def getEnabled(self):
214 return self.enabled
215
216 - def do_stop(self):
217 return self.setEnabled(False)
218
219 - def authenticate(self, keycard):
220 if not self.typeAllowed(keycard): 221 self.warning('keycard %r is not an allowed keycard class', keycard) 222 return None 223 224 if not self.do_validate(keycard): 225 self.warning('keycard %r is not a valid keycard instance', keycard) 226 return None 227 228 if self.enabled: 229 if not self._expirer.running and hasattr(keycard, 'ttl'): 230 self.debug('installing keycard timeout poller') 231 self._expirer.start() 232 return defer.maybeDeferred(self.do_authenticate, keycard) 233 else: 234 self.debug("Bouncer disabled, refusing authentication") 235 return None
236
237 - def do_expireKeycards(self, elapsed):
238 """ 239 Override to expire keycards managed by sub-classes. 240 241 @param elapsed: time in second since the last expiration call. 242 @type elapsed: int 243 @returns: if there is more keycard to expire. If False is returned, 244 the expirer poller MAY be stopped. 245 @rtype: bool 246 """ 247 for k in self._keycards.values(): 248 if hasattr(k, 'ttl'): 249 k.ttl -= elapsed 250 if k.ttl <= 0: 251 self.expireKeycardId(k.id) 252 return len(self._keycards) > 0
253
254 - def do_validate(self, keycard):
255 """ 256 Override to check keycards before authentication steps. 257 Should return True if the keycard is valid, False otherwise. 258 #FIXME: This belong to the base bouncer class 259 260 @param keycard: the keycard that should be validated 261 before authentication 262 @type keycard: flumotion.common.keycards.Keycard 263 @returns: True if the keycard is accepted, False otherwise 264 @rtype: bool 265 """ 266 return True
267
268 - def do_authenticate(self, keycard):
269 """ 270 Must be overridden by subclasses. 271 272 Authenticate the given keycard. 273 Return the keycard with state AUTHENTICATED to authenticate, 274 with state REQUESTING to continue the authentication process, 275 or REFUSED to deny the keycard or a deferred which should 276 have the same eventual value. 277 278 FIXME: Currently, a return value of 'None' is treated 279 as rejecting the keycard. This is unintuitive. 280 281 FIXME: in fact, for authentication sessions like challenge/response, 282 returning a keycard with state REFUSED instead of None 283 will not work properly and may enter in an asynchronous infinit loop. 284 """ 285 raise NotImplementedError("authenticate not overridden")
286
287 - def on_keycardAdded(self, keycard):
288 """ 289 Override to update sub-class specific data related to keycards. 290 Called when the base bouncer accepts and references a new keycard. 291 """
292
293 - def on_keycardRemoved(self, keycard):
294 """ 295 Override to cleanup sub-class specific data related to keycards. 296 Called when the base bouncer has cleanup his references to a keycard. 297 """
298
299 - def on_enabled(self):
300 """ 301 Override to initialize sub-class specific data 302 when the bouncer is enabled. 303 """
304
305 - def on_disabled(self):
306 """ 307 Override to cleanup sub-class specific data 308 when the bouncer is disabled. 309 """
310
311 - def hasKeycard(self, keycard):
312 return keycard in self._keycards.values()
313
314 - def generateKeycardId(self):
315 # FIXME: what if it already had one ? 316 # FIXME: deal with wraparound ? 317 keycardId = self._idFormat % self._idCounter 318 self._idCounter += 1 319 return keycardId
320
321 - def addKeycard(self, keycard):
322 """ 323 Adds a keycard to the bouncer. 324 Can be called with the same keycard more than one time. 325 If the keycard has already been added successfully, 326 adding it again will succeed and return True. 327 328 @param keycard: the keycard to add. 329 @return: if the bouncer accepts the keycard. 330 """ 331 # give keycard an id and store it in our hash 332 if keycard.id in self._keycards: 333 # already in there 334 return True 335 336 keycardId = self.generateKeycardId() 337 keycard.id = keycardId 338 339 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 340 self.log('immediately expiring keycard %r', keycard) 341 return False 342 343 self._addKeycard(keycard) 344 return True
345
346 - def removeKeycard(self, keycard):
347 if not keycard.id in self._keycards: 348 raise KeyError 349 350 del self._keycards[keycard.id] 351 self.on_keycardRemoved(keycard) 352 353 self.info("removed keycard with id %s" % keycard.id)
354
355 - def removeKeycardId(self, keycardId):
356 self.debug("removing keycard with id %s" % keycardId) 357 if not keycardId in self._keycards: 358 raise KeyError 359 360 keycard = self._keycards[keycardId] 361 self.removeKeycard(keycard)
362
363 - def keepAlive(self, issuerName, ttl):
364 for k in self._keycards.itervalues(): 365 if hasattr(k, 'issuerName') and k.issuerName == issuerName: 366 k.ttl = ttl
367
368 - def expireAllKeycards(self):
369 return self.expireKeycardIds(self._keycards.keys())
370
371 - def expireKeycardId(self, keycardId):
372 self.log("expiring keycard with id %r", keycardId) 373 if not keycardId in self._keycards: 374 raise KeyError 375 376 keycard = self._keycards[keycardId] 377 self.removeKeycardId(keycardId) 378 379 if self.medium: 380 return self.medium.callRemote('expireKeycard', 381 keycard.requesterId, keycard.id) 382 else: 383 return defer.succeed(None)
384
385 - def expireKeycardIds(self, keycardIds):
386 self.log("expiring keycards with id %r", keycardIds) 387 d = defer.Deferred() 388 self._expireNextKeycardBlock(0, keycardIds, d) 389 return d
390
391 - def _expireNextKeycardBlock(self, total, keycardIds, finished):
392 keycardBlock = keycardIds[:EXPIRE_BLOCK_SIZE] 393 keycardIds = keycardIds[EXPIRE_BLOCK_SIZE:] 394 idByReq = {} 395 396 for keycardId in keycardBlock: 397 if keycardId in self._keycards: 398 keycard = self._keycards[keycardId] 399 requesterId = keycard.requesterId 400 idByReq.setdefault(requesterId, []).append(keycardId) 401 self.removeKeycardId(keycardId) 402 403 if not (idByReq and self.medium): 404 finished.callback(total) 405 return 406 407 defs = [self.medium.callRemote('expireKeycards', rid, ids) 408 for rid, ids in idByReq.items()] 409 dl = defer.DeferredList(defs, consumeErrors=True) 410 411 def countExpirations(results, total): 412 return sum([v for s, v in results if s and v]) + total
413 414 dl.addCallback(countExpirations, total) 415 dl.addCallback(self._expireNextKeycardBlock, keycardIds, finished) 416
417 - def _addKeycard(self, keycard):
418 """ 419 Adds a keycard without checking. 420 Used by sub-class knowing what they do. 421 """ 422 self._keycards[keycard.id] = keycard 423 self.on_keycardAdded(keycard) 424 425 self.debug("added keycard with id %s, ttl %r", keycard.id, 426 getattr(keycard, 'ttl', None))
427
428 - def _expire(self):
429 if not self.do_expireKeycards(self._expirer.timeout): 430 if self._expirer.running: 431 self.debug('no more keycards, removing timeout poller') 432 self._expirer.stop()
433 434
435 -class AuthSessionBouncer(Bouncer):
436 """ 437 I am a bouncer that handle pending authentication sessions. 438 I am storing the last keycard of an authenticating session. 439 """ 440
441 - def init(self):
442 # Keycards pending to be authenticated 443 self._sessions = {} # keycard id -> (ttl, data)
444
445 - def on_disabled(self):
446 # Removing all pending authentication 447 self._sessions.clear()
448
449 - def do_extractKeycardInfo(self, keycard, oldData):
450 """ 451 Extracts session info from a keycard. 452 Used by updateAuthSession to store session info. 453 Must be overridden by subclasses. 454 """ 455 raise NotImplementedError()
456
457 - def hasAuthSession(self, keycard):
458 """ 459 Tells if a keycard is related to a pending authentication session. 460 It basically check if the id of the keycard is known. 461 462 @param keycard: the keycard to check 463 @type keycard: flumotion.common.keycards.Keycard 464 @returns: if a pending authentication session associated 465 with the specified keycard exists. 466 467 @rtype: bool 468 """ 469 return (keycard.id is not None) and (keycard.id in self._sessions)
470
471 - def getAuthSessionInfo(self, keycard):
472 """ 473 @return: the last updated keycard for the authentication session 474 associated with the specified keycard 475 @rtype: flumotion.common.keycards.Keycard or None 476 """ 477 data = keycard.id and self._sessions.get(keycard.id, None) 478 return data and data[1]
479
480 - def startAuthSession(self, keycard):
481 """ 482 Starts an authentication session with a keycard. 483 The keycard id will be generated and set. 484 The session info will be extracted from the keycard 485 by calling the method do_extractKeycardInfo, and can 486 be retrieved by calling getAuthSessionInfo. 487 488 If a the keycard already have and id, and there is 489 an authentication session with this id, the session info 490 is updated from the keycard, and it return True. 491 492 @param keycard: the keycard to update from. 493 @type keycard: flumotion.common.keycards.Keycard 494 @return: if the bouncer accepts the keycard. 495 """ 496 # Check if there is already an authentication session 497 if self.hasAuthSession(keycard): 498 # Updating the authentication session data 499 self._updateInfoFromKeycard(keycard) 500 return True 501 502 if keycard.id: 503 self.warning("keycard %r already has an id, but no " 504 "authentication session", keycard) 505 keycard.state = keycards.REFUSED 506 return False 507 508 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 509 self.log('immediately expiring keycard %r', keycard) 510 keycard.state = keycards.REFUSED 511 return False 512 513 # Generate an id for the authentication session 514 keycardId = self.generateKeycardId() 515 keycard.id = keycardId 516 517 self._updateInfoFromKeycard(keycard) 518 519 self.debug("started authentication session with with id %s, ttl %r", 520 keycard.id, getattr(keycard, 'ttl', None)) 521 return True
522
523 - def updateAuthSession(self, keycard):
524 """ 525 Updates an authentication session with the last keycard. 526 The session info will be extracted from the keycard 527 by calling the method do_extractKeycardInfo, and can 528 be retrieved by calling getAuthSessionInfo. 529 530 @param keycard: the keycard to update from. 531 @type keycard: flumotion.common.keycards.Keycard 532 """ 533 # Check if there is already an authentication session 534 if self.hasAuthSession(keycard): 535 # Updating the authentication session data 536 self._updateInfoFromKeycard(keycard) 537 else: 538 keycard.state = keycards.REFUSED
539
540 - def cancelAuthSession(self, keycard):
541 """ 542 Cancels the authentication session associated 543 with the specified keycard. 544 Used when doing challenge/response authentication. 545 @raise KeyError: when there is no session associated with the keycard. 546 """ 547 keycard.state = keycards.REFUSED 548 del self._sessions[keycard.id]
549
550 - def confirmAuthSession(self, keycard):
551 """ 552 Confirms the authentication session represented 553 by the specified keycard is authenticated. 554 This will add the specified keycard to the 555 bouncer keycard list like addKeycard would do 556 but without changing the keycard id. 557 The authentication session data is cleaned up. 558 559 If the bouncer already have a keycard with the same id, 560 the authentication is confirmed but the bouncer keycard 561 is NOT updated. FIXME: is it what we want ? ? ? 562 563 @param keycard: the keycard to add to the bouncer list. 564 @type keycard: flumotion.common.keycards.Keycard 565 @return: if the bouncer accepts the keycard. 566 """ 567 keycardId = keycard.id 568 569 if keycardId not in self._sessions: 570 self.warning("unknown authentication session, or pending keycard " 571 "expired for id %s", keycardId) 572 keycard.state = keycards.REFUSED 573 return False 574 575 del self._sessions[keycardId] 576 577 # Check if there already an authenticated keycard with the same id 578 if keycardId in self._keycards: 579 self.debug("confirming an authentication session we already " 580 "know about with id %s", keycardId) 581 keycard.state = keycards.AUTHENTICATED 582 return True 583 584 # check if the keycard already expired 585 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 586 self.log('immediately expiring keycard %r', keycard) 587 keycard.state = keycards.REFUSED 588 return False 589 590 keycard.state = keycards.AUTHENTICATED 591 self._addKeycard(keycard) 592 return True
593
594 - def updateAuthSessionInfo(self, keycard, data):
595 """ 596 Updates the authentication session data. 597 Can be used bu subclasses to modify the data directly. 598 """ 599 ttl, _oldData = self._sessions.get(keycard.id, (None, None)) 600 if ttl is None: 601 ttl = getattr(keycard, 'ttl', None) 602 self._sessions[keycard.id] = (ttl, data)
603
604 - def do_expireKeycards(self, elapsed):
605 cont = Bouncer.do_expireKeycards(self, elapsed) 606 for id, (ttl, data) in self._sessions.items(): 607 if ttl is not None: 608 ttl -= elapsed 609 self._sessions[id] = (ttl, data) 610 if ttl <= 0: 611 del self._sessions[id] 612 613 return cont and len(self._sessions) > 0
614
615 - def _updateInfoFromKeycard(self, keycard):
616 oldData = self.getAuthSessionInfo(keycard) 617 newData = self.do_extractKeycardInfo(keycard, oldData) 618 self.updateAuthSessionInfo(keycard, newData)
619 620
621 -class TrivialBouncer(Bouncer):
622 """ 623 A very trivial bouncer implementation. 624 625 Useful as a concrete bouncer class for which all users are 626 accepted whenever the bouncer is enabled. 627 """ 628 keycardClasses = (keycards.KeycardGeneric, ) 629
630 - def do_authenticate(self, keycard):
631 if self.addKeycard(keycard): 632 keycard.state = keycards.AUTHENTICATED 633 else: 634 keycard.state = keycards.REFUSED 635 return keycard
636 637
638 -class ChallengeResponseBouncer(AuthSessionBouncer):
639 """ 640 A base class for Challenge-Response bouncers 641 """ 642 643 challengeResponseClasses = () 644
645 - def init(self):
646 self._checker = None 647 self._challenges = {} 648 self._db = {}
649
650 - def setChecker(self, checker):
651 self._checker = checker
652
653 - def addUser(self, user, salt, *args):
654 self._db[user] = salt 655 self._checker.addUser(user, *args)
656
657 - def do_extractKeycardInfo(self, keycard, oldData):
658 return getattr(keycard, 'challenge', None)
659
660 - def _requestAvatarIdCallback(self, PossibleAvatarId, keycard):
661 if not self.hasAuthSession(keycard): 662 # The session expired 663 keycard.state = keycards.REFUSED 664 return None 665 666 # authenticated, so return the keycard with state authenticated 667 if not keycard.avatarId: 668 keycard.avatarId = PossibleAvatarId 669 self.info('authenticated login of "%s"' % keycard.avatarId) 670 self.debug('keycard %r authenticated, id %s, avatarId %s' % ( 671 keycard, keycard.id, keycard.avatarId)) 672 self.confirmAuthSession(keycard) 673 keycard.state = keycards.AUTHENTICATED 674 return keycard
675
676 - def _requestAvatarIdErrback(self, failure, keycard):
677 if not self.hasAuthSession(keycard): 678 # The session expired 679 keycard.state = keycards.REFUSED 680 return None 681 682 failure.trap(errors.NotAuthenticatedError) 683 # FIXME: we want to make sure the "None" we return is returned 684 # as coming from a callback, ie the deferred 685 self.info('keycard %r refused, Unauthorized' % keycard) 686 self.cancelAuthSession(keycard) 687 keycard.state = keycards.REFUSED 688 return None
689
690 - def do_authenticate(self, keycard):
691 if isinstance(keycard, self.challengeResponseClasses): 692 # Check if we need to challenge it 693 if not self.hasAuthSession(keycard): 694 if not self.startAuthSession(keycard): 695 # Keycard refused right away 696 keycard.state = keycards.REFUSED 697 return None 698 self.debug('putting challenge on keycard %r' % keycard) 699 keycard.challenge = credentials.cryptChallenge() 700 if keycard.username in self._db: 701 keycard.salt = self._db[keycard.username] 702 else: 703 # random-ish salt, otherwise it's too obvious 704 string = str(random.randint(pow(10, 10), pow(10, 11))) 705 md = python.md5() 706 md.update(string) 707 keycard.salt = md.hexdigest()[:2] 708 self.debug("user not found, inventing bogus salt") 709 self.debug("salt %s, storing challenge for id %s" 710 % (keycard.salt, keycard.id)) 711 self.updateAuthSession(keycard) 712 return keycard 713 else: 714 # Check if the challenge has been tampered with 715 challenge = self.getAuthSessionInfo(keycard) 716 if challenge != keycard.challenge: 717 self.info('keycard %r refused, challenge tampered with' 718 % keycard) 719 self.cancelAuthSession(keycard) 720 keycard.state = keycards.REFUSED 721 return None 722 else: 723 # Not a challenge/response authentication. 724 # creating a temporary session to have a keycard id 725 if not self.startAuthSession(keycard): 726 # Keycard refused right away 727 keycard.state = keycards.REFUSED 728 return None 729 730 # use the checker 731 self.debug('submitting keycard %r to checker' % keycard) 732 d = self._checker.requestAvatarId(keycard) 733 d.addCallback(self._requestAvatarIdCallback, keycard) 734 d.addErrback(self._requestAvatarIdErrback, keycard) 735 return d
736