test_jabbercomponent.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.words.protocols.jabber.component}
  5. """
  6. from hashlib import sha1
  7. from zope.interface.verify import verifyObject
  8. from twisted.python import failure
  9. from twisted.python.compat import unicode
  10. from twisted.trial import unittest
  11. from twisted.words.protocols.jabber import component, ijabber, xmlstream
  12. from twisted.words.protocols.jabber.jid import JID
  13. from twisted.words.xish import domish
  14. from twisted.words.xish.utility import XmlPipe
  15. class DummyTransport:
  16. def __init__(self, list):
  17. self.list = list
  18. def write(self, bytes):
  19. self.list.append(bytes)
  20. class ComponentInitiatingInitializerTests(unittest.TestCase):
  21. def setUp(self):
  22. self.output = []
  23. self.authenticator = xmlstream.Authenticator()
  24. self.authenticator.password = u'secret'
  25. self.xmlstream = xmlstream.XmlStream(self.authenticator)
  26. self.xmlstream.namespace = 'test:component'
  27. self.xmlstream.send = self.output.append
  28. self.xmlstream.connectionMade()
  29. self.xmlstream.dataReceived(
  30. "<stream:stream xmlns='test:component' "
  31. "xmlns:stream='http://etherx.jabber.org/streams' "
  32. "from='example.com' id='12345' version='1.0'>")
  33. self.xmlstream.sid = u'12345'
  34. self.init = component.ComponentInitiatingInitializer(self.xmlstream)
  35. def testHandshake(self):
  36. """
  37. Test basic operations of component handshake.
  38. """
  39. d = self.init.initialize()
  40. # the initializer should have sent the handshake request
  41. handshake = self.output[-1]
  42. self.assertEqual('handshake', handshake.name)
  43. self.assertEqual('test:component', handshake.uri)
  44. self.assertEqual(sha1(b'12345' + b'secret').hexdigest(),
  45. unicode(handshake))
  46. # successful authentication
  47. handshake.children = []
  48. self.xmlstream.dataReceived(handshake.toXml())
  49. return d
  50. class ComponentAuthTests(unittest.TestCase):
  51. def authPassed(self, stream):
  52. self.authComplete = True
  53. def testAuth(self):
  54. self.authComplete = False
  55. outlist = []
  56. ca = component.ConnectComponentAuthenticator(u"cjid", u"secret")
  57. xs = xmlstream.XmlStream(ca)
  58. xs.transport = DummyTransport(outlist)
  59. xs.addObserver(xmlstream.STREAM_AUTHD_EVENT,
  60. self.authPassed)
  61. # Go...
  62. xs.connectionMade()
  63. xs.dataReceived(b"<stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' from='cjid' id='12345'>")
  64. # Calculate what we expect the handshake value to be
  65. hv = sha1(b"12345" + b"secret").hexdigest().encode('ascii')
  66. self.assertEqual(outlist[1], b"<handshake>" + hv + b"</handshake>")
  67. xs.dataReceived("<handshake/>")
  68. self.assertEqual(self.authComplete, True)
  69. class ServiceTests(unittest.TestCase):
  70. """
  71. Tests for L{component.Service}.
  72. """
  73. def test_interface(self):
  74. """
  75. L{component.Service} implements L{ijabber.IService}.
  76. """
  77. service = component.Service()
  78. verifyObject(ijabber.IService, service)
  79. class JabberServiceHarness(component.Service):
  80. def __init__(self):
  81. self.componentConnectedFlag = False
  82. self.componentDisconnectedFlag = False
  83. self.transportConnectedFlag = False
  84. def componentConnected(self, xmlstream):
  85. self.componentConnectedFlag = True
  86. def componentDisconnected(self):
  87. self.componentDisconnectedFlag = True
  88. def transportConnected(self, xmlstream):
  89. self.transportConnectedFlag = True
  90. class JabberServiceManagerTests(unittest.TestCase):
  91. def testSM(self):
  92. # Setup service manager and test harnes
  93. sm = component.ServiceManager("foo", "password")
  94. svc = JabberServiceHarness()
  95. svc.setServiceParent(sm)
  96. # Create a write list
  97. wlist = []
  98. # Setup a XmlStream
  99. xs = sm.getFactory().buildProtocol(None)
  100. xs.transport = self
  101. xs.transport.write = wlist.append
  102. # Indicate that it's connected
  103. xs.connectionMade()
  104. # Ensure the test service harness got notified
  105. self.assertEqual(True, svc.transportConnectedFlag)
  106. # Jump ahead and pretend like the stream got auth'd
  107. xs.dispatch(xs, xmlstream.STREAM_AUTHD_EVENT)
  108. # Ensure the test service harness got notified
  109. self.assertEqual(True, svc.componentConnectedFlag)
  110. # Pretend to drop the connection
  111. xs.connectionLost(None)
  112. # Ensure the test service harness got notified
  113. self.assertEqual(True, svc.componentDisconnectedFlag)
  114. class RouterTests(unittest.TestCase):
  115. """
  116. Tests for L{component.Router}.
  117. """
  118. def test_addRoute(self):
  119. """
  120. Test route registration and routing on incoming stanzas.
  121. """
  122. router = component.Router()
  123. routed = []
  124. router.route = lambda element: routed.append(element)
  125. pipe = XmlPipe()
  126. router.addRoute('example.org', pipe.sink)
  127. self.assertEqual(1, len(router.routes))
  128. self.assertEqual(pipe.sink, router.routes['example.org'])
  129. element = domish.Element(('testns', 'test'))
  130. pipe.source.send(element)
  131. self.assertEqual([element], routed)
  132. def test_route(self):
  133. """
  134. Test routing of a message.
  135. """
  136. component1 = XmlPipe()
  137. component2 = XmlPipe()
  138. router = component.Router()
  139. router.addRoute('component1.example.org', component1.sink)
  140. router.addRoute('component2.example.org', component2.sink)
  141. outgoing = []
  142. component2.source.addObserver('/*',
  143. lambda element: outgoing.append(element))
  144. stanza = domish.Element((None, 'presence'))
  145. stanza['from'] = 'component1.example.org'
  146. stanza['to'] = 'component2.example.org'
  147. component1.source.send(stanza)
  148. self.assertEqual([stanza], outgoing)
  149. def test_routeDefault(self):
  150. """
  151. Test routing of a message using the default route.
  152. The default route is the one with L{None} as its key in the
  153. routing table. It is taken when there is no more specific route
  154. in the routing table that matches the stanza's destination.
  155. """
  156. component1 = XmlPipe()
  157. s2s = XmlPipe()
  158. router = component.Router()
  159. router.addRoute('component1.example.org', component1.sink)
  160. router.addRoute(None, s2s.sink)
  161. outgoing = []
  162. s2s.source.addObserver('/*', lambda element: outgoing.append(element))
  163. stanza = domish.Element((None, 'presence'))
  164. stanza['from'] = 'component1.example.org'
  165. stanza['to'] = 'example.com'
  166. component1.source.send(stanza)
  167. self.assertEqual([stanza], outgoing)
  168. class ListenComponentAuthenticatorTests(unittest.TestCase):
  169. """
  170. Tests for L{component.ListenComponentAuthenticator}.
  171. """
  172. def setUp(self):
  173. self.output = []
  174. authenticator = component.ListenComponentAuthenticator('secret')
  175. self.xmlstream = xmlstream.XmlStream(authenticator)
  176. self.xmlstream.send = self.output.append
  177. def loseConnection(self):
  178. """
  179. Stub loseConnection because we are a transport.
  180. """
  181. self.xmlstream.connectionLost("no reason")
  182. def test_streamStarted(self):
  183. """
  184. The received stream header should set several attributes.
  185. """
  186. observers = []
  187. def addOnetimeObserver(event, observerfn):
  188. observers.append((event, observerfn))
  189. xs = self.xmlstream
  190. xs.addOnetimeObserver = addOnetimeObserver
  191. xs.makeConnection(self)
  192. self.assertIdentical(None, xs.sid)
  193. self.assertFalse(xs._headerSent)
  194. xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
  195. "xmlns:stream='http://etherx.jabber.org/streams' "
  196. "to='component.example.org'>")
  197. self.assertEqual((0, 0), xs.version)
  198. self.assertNotIdentical(None, xs.sid)
  199. self.assertTrue(xs._headerSent)
  200. self.assertEqual(('/*', xs.authenticator.onElement), observers[-1])
  201. def test_streamStartedWrongNamespace(self):
  202. """
  203. The received stream header should have a correct namespace.
  204. """
  205. streamErrors = []
  206. xs = self.xmlstream
  207. xs.sendStreamError = streamErrors.append
  208. xs.makeConnection(self)
  209. xs.dataReceived("<stream:stream xmlns='jabber:client' "
  210. "xmlns:stream='http://etherx.jabber.org/streams' "
  211. "to='component.example.org'>")
  212. self.assertEqual(1, len(streamErrors))
  213. self.assertEqual('invalid-namespace', streamErrors[-1].condition)
  214. def test_streamStartedNoTo(self):
  215. """
  216. The received stream header should have a 'to' attribute.
  217. """
  218. streamErrors = []
  219. xs = self.xmlstream
  220. xs.sendStreamError = streamErrors.append
  221. xs.makeConnection(self)
  222. xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
  223. "xmlns:stream='http://etherx.jabber.org/streams'>")
  224. self.assertEqual(1, len(streamErrors))
  225. self.assertEqual('improper-addressing', streamErrors[-1].condition)
  226. def test_onElement(self):
  227. """
  228. We expect a handshake element with a hash.
  229. """
  230. handshakes = []
  231. xs = self.xmlstream
  232. xs.authenticator.onHandshake = handshakes.append
  233. handshake = domish.Element(('jabber:component:accept', 'handshake'))
  234. handshake.addContent(u'1234')
  235. xs.authenticator.onElement(handshake)
  236. self.assertEqual('1234', handshakes[-1])
  237. def test_onElementNotHandshake(self):
  238. """
  239. Reject elements that are not handshakes
  240. """
  241. handshakes = []
  242. streamErrors = []
  243. xs = self.xmlstream
  244. xs.authenticator.onHandshake = handshakes.append
  245. xs.sendStreamError = streamErrors.append
  246. element = domish.Element(('jabber:component:accept', 'message'))
  247. xs.authenticator.onElement(element)
  248. self.assertFalse(handshakes)
  249. self.assertEqual('not-authorized', streamErrors[-1].condition)
  250. def test_onHandshake(self):
  251. """
  252. Receiving a handshake matching the secret authenticates the stream.
  253. """
  254. authd = []
  255. def authenticated(xs):
  256. authd.append(xs)
  257. xs = self.xmlstream
  258. xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
  259. xs.sid = u'1234'
  260. theHash = '32532c0f7dbf1253c095b18b18e36d38d94c1256'
  261. xs.authenticator.onHandshake(theHash)
  262. self.assertEqual('<handshake/>', self.output[-1])
  263. self.assertEqual(1, len(authd))
  264. def test_onHandshakeWrongHash(self):
  265. """
  266. Receiving a bad handshake should yield a stream error.
  267. """
  268. streamErrors = []
  269. authd = []
  270. def authenticated(xs):
  271. authd.append(xs)
  272. xs = self.xmlstream
  273. xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
  274. xs.sendStreamError = streamErrors.append
  275. xs.sid = u'1234'
  276. theHash = '1234'
  277. xs.authenticator.onHandshake(theHash)
  278. self.assertEqual('not-authorized', streamErrors[-1].condition)
  279. self.assertEqual(0, len(authd))
  280. class XMPPComponentServerFactoryTests(unittest.TestCase):
  281. """
  282. Tests for L{component.XMPPComponentServerFactory}.
  283. """
  284. def setUp(self):
  285. self.router = component.Router()
  286. self.factory = component.XMPPComponentServerFactory(self.router,
  287. 'secret')
  288. self.xmlstream = self.factory.buildProtocol(None)
  289. self.xmlstream.thisEntity = JID('component.example.org')
  290. def test_makeConnection(self):
  291. """
  292. A new connection increases the stream serial count. No logs by default.
  293. """
  294. self.xmlstream.dispatch(self.xmlstream,
  295. xmlstream.STREAM_CONNECTED_EVENT)
  296. self.assertEqual(0, self.xmlstream.serial)
  297. self.assertEqual(1, self.factory.serial)
  298. self.assertIdentical(None, self.xmlstream.rawDataInFn)
  299. self.assertIdentical(None, self.xmlstream.rawDataOutFn)
  300. def test_makeConnectionLogTraffic(self):
  301. """
  302. Setting logTraffic should set up raw data loggers.
  303. """
  304. self.factory.logTraffic = True
  305. self.xmlstream.dispatch(self.xmlstream,
  306. xmlstream.STREAM_CONNECTED_EVENT)
  307. self.assertNotIdentical(None, self.xmlstream.rawDataInFn)
  308. self.assertNotIdentical(None, self.xmlstream.rawDataOutFn)
  309. def test_onError(self):
  310. """
  311. An observer for stream errors should trigger onError to log it.
  312. """
  313. self.xmlstream.dispatch(self.xmlstream,
  314. xmlstream.STREAM_CONNECTED_EVENT)
  315. class TestError(Exception):
  316. pass
  317. reason = failure.Failure(TestError())
  318. self.xmlstream.dispatch(reason, xmlstream.STREAM_ERROR_EVENT)
  319. self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
  320. def test_connectionInitialized(self):
  321. """
  322. Make sure a new stream is added to the routing table.
  323. """
  324. self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
  325. self.assertIn('component.example.org', self.router.routes)
  326. self.assertIdentical(self.xmlstream,
  327. self.router.routes['component.example.org'])
  328. def test_connectionLost(self):
  329. """
  330. Make sure a stream is removed from the routing table on disconnect.
  331. """
  332. self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
  333. self.xmlstream.dispatch(None, xmlstream.STREAM_END_EVENT)
  334. self.assertNotIn('component.example.org', self.router.routes)