You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

portal.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. # -*- test-case-name: twisted.cred.test.test_cred -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. The point of integration of application and authentication.
  6. """
  7. from __future__ import division, absolute_import
  8. from twisted.internet import defer
  9. from twisted.internet.defer import maybeDeferred
  10. from twisted.python import failure, reflect
  11. from twisted.cred import error
  12. from zope.interface import providedBy, Interface
  13. class IRealm(Interface):
  14. """
  15. The realm connects application-specific objects to the
  16. authentication system.
  17. """
  18. def requestAvatar(avatarId, mind, *interfaces):
  19. """
  20. Return avatar which provides one of the given interfaces.
  21. @param avatarId: a string that identifies an avatar, as returned by
  22. L{ICredentialsChecker.requestAvatarId<twisted.cred.checkers.ICredentialsChecker.requestAvatarId>}
  23. (via a Deferred). Alternatively, it may be
  24. C{twisted.cred.checkers.ANONYMOUS}.
  25. @param mind: usually None. See the description of mind in
  26. L{Portal.login}.
  27. @param interfaces: the interface(s) the returned avatar should
  28. implement, e.g. C{IMailAccount}. See the description of
  29. L{Portal.login}.
  30. @returns: a deferred which will fire a tuple of (interface,
  31. avatarAspect, logout), or the tuple itself. The interface will be
  32. one of the interfaces passed in the 'interfaces' argument. The
  33. 'avatarAspect' will implement that interface. The 'logout' object
  34. is a callable which will detach the mind from the avatar.
  35. """
  36. class Portal(object):
  37. """
  38. A mediator between clients and a realm.
  39. A portal is associated with one Realm and zero or more credentials checkers.
  40. When a login is attempted, the portal finds the appropriate credentials
  41. checker for the credentials given, invokes it, and if the credentials are
  42. valid, retrieves the appropriate avatar from the Realm.
  43. This class is not intended to be subclassed. Customization should be done
  44. in the realm object and in the credentials checker objects.
  45. """
  46. def __init__(self, realm, checkers=()):
  47. """
  48. Create a Portal to a L{IRealm}.
  49. """
  50. self.realm = realm
  51. self.checkers = {}
  52. for checker in checkers:
  53. self.registerChecker(checker)
  54. def listCredentialsInterfaces(self):
  55. """
  56. Return list of credentials interfaces that can be used to login.
  57. """
  58. return list(self.checkers.keys())
  59. def registerChecker(self, checker, *credentialInterfaces):
  60. if not credentialInterfaces:
  61. credentialInterfaces = checker.credentialInterfaces
  62. for credentialInterface in credentialInterfaces:
  63. self.checkers[credentialInterface] = checker
  64. def login(self, credentials, mind, *interfaces):
  65. """
  66. @param credentials: an implementor of
  67. L{twisted.cred.credentials.ICredentials}
  68. @param mind: an object which implements a client-side interface for
  69. your particular realm. In many cases, this may be None, so if the
  70. word 'mind' confuses you, just ignore it.
  71. @param interfaces: list of interfaces for the perspective that the mind
  72. wishes to attach to. Usually, this will be only one interface, for
  73. example IMailAccount. For highly dynamic protocols, however, this
  74. may be a list like (IMailAccount, IUserChooser, IServiceInfo). To
  75. expand: if we are speaking to the system over IMAP, any information
  76. that will be relayed to the user MUST be returned as an
  77. IMailAccount implementor; IMAP clients would not be able to
  78. understand anything else. Any information about unusual status
  79. would have to be relayed as a single mail message in an
  80. otherwise-empty mailbox. However, in a web-based mail system, or a
  81. PB-based client, the ``mind'' object inside the web server
  82. (implemented with a dynamic page-viewing mechanism such as a
  83. Twisted Web Resource) or on the user's client program may be
  84. intelligent enough to respond to several ``server''-side
  85. interfaces.
  86. @return: A deferred which will fire a tuple of (interface,
  87. avatarAspect, logout). The interface will be one of the interfaces
  88. passed in the 'interfaces' argument. The 'avatarAspect' will
  89. implement that interface. The 'logout' object is a callable which
  90. will detach the mind from the avatar. It must be called when the
  91. user has conceptually disconnected from the service. Although in
  92. some cases this will not be in connectionLost (such as in a
  93. web-based session), it will always be at the end of a user's
  94. interactive session.
  95. """
  96. for i in self.checkers:
  97. if i.providedBy(credentials):
  98. return maybeDeferred(self.checkers[i].requestAvatarId, credentials
  99. ).addCallback(self.realm.requestAvatar, mind, *interfaces
  100. )
  101. ifac = providedBy(credentials)
  102. return defer.fail(failure.Failure(error.UnhandledCredentials(
  103. "No checker for %s" % ', '.join(map(reflect.qual, ifac)))))