manhole_ssh.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # -*- test-case-name: twisted.conch.test.test_manhole -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. insults/SSH integration support.
  6. @author: Jp Calderone
  7. """
  8. from zope.interface import implementer
  9. from twisted.conch import avatar, interfaces as iconch, error as econch
  10. from twisted.conch.ssh import factory, session
  11. from twisted.python import components
  12. from twisted.conch.insults import insults
  13. class _Glue:
  14. """
  15. A feeble class for making one attribute look like another.
  16. This should be replaced with a real class at some point, probably.
  17. Try not to write new code that uses it.
  18. """
  19. def __init__(self, **kw):
  20. self.__dict__.update(kw)
  21. def __getattr__(self, name):
  22. raise AttributeError(self.name, "has no attribute", name)
  23. class TerminalSessionTransport:
  24. def __init__(self, proto, chainedProtocol, avatar, width, height):
  25. self.proto = proto
  26. self.avatar = avatar
  27. self.chainedProtocol = chainedProtocol
  28. protoSession = self.proto.session
  29. self.proto.makeConnection(
  30. _Glue(write=self.chainedProtocol.dataReceived,
  31. loseConnection=lambda: avatar.conn.sendClose(protoSession),
  32. name="SSH Proto Transport"))
  33. def loseConnection():
  34. self.proto.loseConnection()
  35. self.chainedProtocol.makeConnection(
  36. _Glue(write=self.proto.write,
  37. loseConnection=loseConnection,
  38. name="Chained Proto Transport"))
  39. # XXX TODO
  40. # chainedProtocol is supposed to be an ITerminalTransport,
  41. # maybe. That means perhaps its terminalProtocol attribute is
  42. # an ITerminalProtocol, it could be. So calling terminalSize
  43. # on that should do the right thing But it'd be nice to clean
  44. # this bit up.
  45. self.chainedProtocol.terminalProtocol.terminalSize(width, height)
  46. @implementer(iconch.ISession)
  47. class TerminalSession(components.Adapter):
  48. transportFactory = TerminalSessionTransport
  49. chainedProtocolFactory = insults.ServerProtocol
  50. def getPty(self, term, windowSize, attrs):
  51. self.height, self.width = windowSize[:2]
  52. def openShell(self, proto):
  53. self.transportFactory(
  54. proto, self.chainedProtocolFactory(),
  55. iconch.IConchUser(self.original),
  56. self.width, self.height)
  57. def execCommand(self, proto, cmd):
  58. raise econch.ConchError("Cannot execute commands")
  59. def closed(self):
  60. pass
  61. class TerminalUser(avatar.ConchUser, components.Adapter):
  62. def __init__(self, original, avatarId):
  63. components.Adapter.__init__(self, original)
  64. avatar.ConchUser.__init__(self)
  65. self.channelLookup[b'session'] = session.SSHSession
  66. class TerminalRealm:
  67. userFactory = TerminalUser
  68. sessionFactory = TerminalSession
  69. transportFactory = TerminalSessionTransport
  70. chainedProtocolFactory = insults.ServerProtocol
  71. def _getAvatar(self, avatarId):
  72. comp = components.Componentized()
  73. user = self.userFactory(comp, avatarId)
  74. sess = self.sessionFactory(comp)
  75. sess.transportFactory = self.transportFactory
  76. sess.chainedProtocolFactory = self.chainedProtocolFactory
  77. comp.setComponent(iconch.IConchUser, user)
  78. comp.setComponent(iconch.ISession, sess)
  79. return user
  80. def __init__(self, transportFactory=None):
  81. if transportFactory is not None:
  82. self.transportFactory = transportFactory
  83. def requestAvatar(self, avatarId, mind, *interfaces):
  84. for i in interfaces:
  85. if i is iconch.IConchUser:
  86. return (iconch.IConchUser,
  87. self._getAvatar(avatarId),
  88. lambda: None)
  89. raise NotImplementedError()
  90. class ConchFactory(factory.SSHFactory):
  91. publicKeys = {}
  92. privateKeys = {}
  93. def __init__(self, portal):
  94. self.portal = portal