source: wokkel/test/test_server.py @ 96:8e6130587088

Last change on this file since 96:8e6130587088 was 96:8e6130587088, checked in by Ralph Meijer <ralphm@…>, 10 years ago

Remove copyright dates from individual source files, only update LICENSE.

  • Property exe set to *
File size: 15.0 KB
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.server}.
6"""
7
8from twisted.internet import defer
9from twisted.python import failure
10from twisted.test.proto_helpers import StringTransport
11from twisted.trial import unittest
12from twisted.words.protocols.jabber import error, jid, xmlstream
13from twisted.words.xish import domish
14
15from wokkel import component, server
16
17NS_STREAMS = 'http://etherx.jabber.org/streams'
18NS_DIALBACK = "jabber:server:dialback"
19
20class GenerateKeyTest(unittest.TestCase):
21    """
22    Tests for L{server.generateKey}.
23    """
24
25    def testBasic(self):
26        originating = "example.org"
27        receiving = "xmpp.example.com"
28        sid = "D60000229F"
29        secret = "s3cr3tf0rd14lb4ck"
30
31        key = server.generateKey(secret, receiving, originating, sid)
32
33        self.assertEqual(key,
34            '37c69b1cf07a3f67c04a5ef5902fa5114f2c76fe4a2686482ba5b89323075643')
35
36
37
38class XMPPServerListenAuthenticatorTest(unittest.TestCase):
39    """
40    Tests for L{server.XMPPServerListenAuthenticator}.
41    """
42
43    secret = "s3cr3tf0rd14lb4ck"
44    originating = "example.org"
45    receiving = "xmpp.example.com"
46    sid = "D60000229F"
47    key = '37c69b1cf07a3f67c04a5ef5902fa5114f2c76fe4a2686482ba5b89323075643'
48
49    def setUp(self):
50        self.output = []
51
52        class MyService(object):
53            pass
54
55        self.service = MyService()
56        self.service.defaultDomain = self.receiving
57        self.service.domains = [self.receiving, 'pubsub.'+self.receiving]
58        self.service.secret = self.secret
59
60        self.authenticator = server.XMPPServerListenAuthenticator(self.service)
61        self.xmlstream = xmlstream.XmlStream(self.authenticator)
62        self.xmlstream.send = self.output.append
63        self.xmlstream.transport = StringTransport()
64
65
66    def test_attributes(self):
67        """
68        Test attributes of authenticator and stream objects.
69        """
70        self.assertEqual(self.service, self.authenticator.service)
71        self.assertEqual(self.xmlstream.initiating, False)
72
73
74    def test_streamStartedVersion0(self):
75        """
76        The authenticator supports pre-XMPP 1.0 streams.
77        """
78        self.xmlstream.connectionMade()
79        self.xmlstream.dataReceived(
80            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
81                           "xmlns:db='jabber:server:dialback' "
82                           "xmlns='jabber:server' "
83                           "to='xmpp.example.com'>")
84        self.assertEqual((0, 0), self.xmlstream.version)
85
86
87    def test_streamStartedVersion1(self):
88        """
89        The authenticator supports XMPP 1.0 streams.
90        """
91        self.xmlstream.connectionMade()
92        self.xmlstream.dataReceived(
93            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
94                           "xmlns:db='jabber:server:dialback' "
95                           "xmlns='jabber:server' "
96                           "to='xmpp.example.com' "
97                           "version='1.0'>")
98        self.assertEqual((1, 0), self.xmlstream.version)
99
100
101    def test_streamStartedSID(self):
102        """
103        The response stream will have a stream ID.
104        """
105        self.xmlstream.connectionMade()
106        self.assertIdentical(None, self.xmlstream.sid)
107
108        self.xmlstream.dataReceived(
109            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
110                           "xmlns:db='jabber:server:dialback' "
111                           "xmlns='jabber:server' "
112                           "to='xmpp.example.com' "
113                           "version='1.0'>")
114        self.assertNotIdentical(None, self.xmlstream.sid)
115
116
117    def test_streamStartedSentResponseHeader(self):
118        """
119        A stream header is sent in response to the incoming stream header.
120        """
121        self.xmlstream.connectionMade()
122        self.assertFalse(self.xmlstream._headerSent)
123
124        self.xmlstream.dataReceived(
125            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
126                           "xmlns:db='jabber:server:dialback' "
127                           "xmlns='jabber:server' "
128                           "to='xmpp.example.com'>")
129        self.assertTrue(self.xmlstream._headerSent)
130
131
132    def test_streamStartedNotSentFeatures(self):
133        """
134        No features are sent in response to an XMPP < 1.0 stream header.
135        """
136        self.xmlstream.connectionMade()
137        self.xmlstream.dataReceived(
138            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
139                           "xmlns:db='jabber:server:dialback' "
140                           "xmlns='jabber:server' "
141                           "to='xmpp.example.com'>")
142        self.assertEqual(1, len(self.output))
143
144
145    def test_streamStartedSentFeatures(self):
146        """
147        Features are sent in response to an XMPP >= 1.0 stream header.
148        """
149        self.xmlstream.connectionMade()
150        self.xmlstream.dataReceived(
151            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
152                           "xmlns:db='jabber:server:dialback' "
153                           "xmlns='jabber:server' "
154                           "to='xmpp.example.com' "
155                           "version='1.0'>")
156        self.assertEqual(2, len(self.output))
157        features = self.output[-1]
158        self.assertEqual(NS_STREAMS, features.uri)
159        self.assertEqual('features', features.name)
160
161
162    def test_streamRootElement(self):
163        """
164        Test stream error on wrong stream namespace.
165        """
166        self.xmlstream.connectionMade()
167        self.xmlstream.dataReceived(
168            "<stream:stream xmlns:stream='badns' "
169                           "xmlns:db='jabber:server:dialback' "
170                           "xmlns='jabber:server' "
171                           "to='xmpp.example.com'>")
172
173        self.assertEqual(3, len(self.output))
174        exc = error.exceptionFromStreamError(self.output[1])
175        self.assertEqual('invalid-namespace', exc.condition)
176
177
178    def test_streamDefaultNamespace(self):
179        """
180        Test stream error on missing dialback namespace.
181        """
182        self.xmlstream.connectionMade()
183        self.xmlstream.dataReceived(
184            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
185                           "xmlns:db='jabber:server:dialback' "
186                           "xmlns='badns' "
187                           "to='xmpp.example.com'>")
188
189        self.assertEqual(3, len(self.output))
190        exc = error.exceptionFromStreamError(self.output[1])
191        self.assertEqual('invalid-namespace', exc.condition)
192
193
194    def test_streamNoDialbackNamespace(self):
195        """
196        Test stream error on missing dialback namespace.
197        """
198        self.xmlstream.connectionMade()
199        self.xmlstream.dataReceived(
200            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
201                           "xmlns='jabber:server' "
202                           "to='xmpp.example.com'>")
203
204        self.assertEqual(3, len(self.output))
205        exc = error.exceptionFromStreamError(self.output[1])
206        self.assertEqual('invalid-namespace', exc.condition)
207
208
209    def test_streamBadDialbackNamespace(self):
210        """
211        Test stream error on missing dialback namespace.
212        """
213        self.xmlstream.connectionMade()
214        self.xmlstream.dataReceived(
215            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
216                           "xmlns:db='badns' "
217                           "xmlns='jabber:server' "
218                           "to='xmpp.example.com'>")
219
220        self.assertEqual(3, len(self.output))
221        exc = error.exceptionFromStreamError(self.output[1])
222        self.assertEqual('invalid-namespace', exc.condition)
223
224
225    def test_streamToUnknownHost(self):
226        """
227        Test stream error on stream's to attribute having unknown host.
228        """
229        self.xmlstream.connectionMade()
230        self.xmlstream.dataReceived(
231            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
232                           "xmlns:db='jabber:server:dialback' "
233                           "xmlns='jabber:server' "
234                           "to='badhost'>")
235
236        self.assertEqual(3, len(self.output))
237        exc = error.exceptionFromStreamError(self.output[1])
238        self.assertEqual('host-unknown', exc.condition)
239
240
241    def test_streamToOtherLocalHost(self):
242        """
243        The authenticator supports XMPP 1.0 streams.
244        """
245        self.xmlstream.connectionMade()
246        self.xmlstream.dataReceived(
247            "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
248                           "xmlns:db='jabber:server:dialback' "
249                           "xmlns='jabber:server' "
250                           "to='pubsub.xmpp.example.com' "
251                           "version='1.0'>")
252
253        self.assertEqual(2, len(self.output))
254        self.assertEqual(jid.JID('pubsub.xmpp.example.com'),
255                         self.xmlstream.thisEntity)
256
257    def test_onResult(self):
258        def cb(result):
259            self.assertEqual(1, len(self.output))
260            reply = self.output[0]
261            self.assertEqual(self.originating, reply['to'])
262            self.assertEqual(self.receiving, reply['from'])
263            self.assertEqual('valid', reply['type'])
264
265        def validateConnection(thisHost, otherHost, sid, key):
266            self.assertEqual(thisHost, self.receiving)
267            self.assertEqual(otherHost, self.originating)
268            self.assertEqual(sid, self.sid)
269            self.assertEqual(key, self.key)
270            return defer.succeed(None)
271
272        self.xmlstream.sid = self.sid
273        self.service.validateConnection = validateConnection
274
275        result = domish.Element((NS_DIALBACK, 'result'))
276        result['to'] = self.receiving
277        result['from'] = self.originating
278        result.addContent(self.key)
279
280        d = self.authenticator.onResult(result)
281        d.addCallback(cb)
282        return d
283
284
285    def test_onResultFailure(self):
286        class TestError(Exception):
287            pass
288
289        def cb(result):
290            reply = self.output[0]
291            self.assertEqual('invalid', reply['type'])
292            self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
293
294
295        def validateConnection(thisHost, otherHost, sid, key):
296            return defer.fail(TestError())
297
298        self.xmlstream.sid = self.sid
299        self.service.validateConnection = validateConnection
300
301        result = domish.Element((NS_DIALBACK, 'result'))
302        result['to'] = self.receiving
303        result['from'] = self.originating
304        result.addContent(self.key)
305
306        d = self.authenticator.onResult(result)
307        d.addCallback(cb)
308        return d
309
310
311
312class FakeService(object):
313    domains = set(['example.org', 'pubsub.example.org'])
314    defaultDomain = 'example.org'
315    secret = 'mysecret'
316
317    def __init__(self):
318        self.dispatched = []
319
320    def dispatch(self, xs, element):
321        self.dispatched.append(element)
322
323
324
325class XMPPS2SServerFactoryTest(unittest.TestCase):
326    """
327    Tests for L{component.XMPPS2SServerFactory}.
328    """
329
330    def setUp(self):
331        self.service = FakeService()
332        self.factory = server.XMPPS2SServerFactory(self.service)
333        self.xmlstream = self.factory.buildProtocol(None)
334        self.transport = StringTransport()
335        self.xmlstream.thisEntity = jid.JID('example.org')
336        self.xmlstream.otherEntity = jid.JID('example.com')
337
338
339    def test_makeConnection(self):
340        """
341        A new connection increases the stream serial count. No logs by default.
342        """
343        self.xmlstream.makeConnection(self.transport)
344        self.assertEqual(0, self.xmlstream.serial)
345        self.assertEqual(1, self.factory.serial)
346        self.assertIdentical(None, self.xmlstream.rawDataInFn)
347        self.assertIdentical(None, self.xmlstream.rawDataOutFn)
348
349
350    def test_makeConnectionLogTraffic(self):
351        """
352        Setting logTraffic should set up raw data loggers.
353        """
354        self.factory.logTraffic = True
355        self.xmlstream.makeConnection(self.transport)
356        self.assertNotIdentical(None, self.xmlstream.rawDataInFn)
357        self.assertNotIdentical(None, self.xmlstream.rawDataOutFn)
358
359
360    def test_onError(self):
361        """
362        An observer for stream errors should trigger onError to log it.
363        """
364        self.xmlstream.makeConnection(self.transport)
365
366        class TestError(Exception):
367            pass
368
369        reason = failure.Failure(TestError())
370        self.xmlstream.dispatch(reason, xmlstream.STREAM_ERROR_EVENT)
371        self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
372
373
374    def test_connectionInitialized(self):
375        """
376        """
377        self.xmlstream.makeConnection(self.transport)
378        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
379
380
381    def test_connectionLost(self):
382        """
383        """
384        self.xmlstream.makeConnection(self.transport)
385        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
386        self.xmlstream.dispatch(None, xmlstream.STREAM_END_EVENT)
387
388
389    def test_Element(self):
390        self.xmlstream.makeConnection(self.transport)
391        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
392
393        stanza = domish.Element((None, "presence"))
394        self.xmlstream.dispatch(stanza)
395        self.assertEqual(1, len(self.service.dispatched))
396        self.assertIdentical(stanza, self.service.dispatched[-1])
397
398
399    def test_ElementNotAuthenticated(self):
400        self.xmlstream.makeConnection(self.transport)
401
402        stanza = domish.Element((None, "presence"))
403        self.xmlstream.dispatch(stanza)
404        self.assertEqual(0, len(self.service.dispatched))
405
406
407
408class ServerServiceTest(unittest.TestCase):
409
410    def setUp(self):
411        self.output = []
412
413        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
414        self.xmlstream.thisEntity = jid.JID('example.org')
415        self.xmlstream.otherEntity = jid.JID('example.com')
416        self.xmlstream.send = self.output.append
417
418        self.router = component.Router()
419        self.service = server.ServerService(self.router,
420                                            secret='mysecret',
421                                            domain='example.org')
422        self.service.xmlstream = self.xmlstream
423
424
425    def test_defaultDomainInDomains(self):
426        """
427        The default domain is part of the domains considered local.
428        """
429        self.assertIn(self.service.defaultDomain, self.service.domains)
430
431
432    def test_dispatch(self):
433        stanza = domish.Element((None, "presence"))
434        stanza['to'] = 'user@example.org'
435        stanza['from'] = 'other@example.com'
436        self.service.dispatch(self.xmlstream, stanza)
437
438        self.assertEqual(1, len(self.output))
439        self.assertIdentical(stanza, self.output[-1])
440
441
442    def test_dispatchNoTo(self):
443        errors = []
444        self.xmlstream.sendStreamError = errors.append
445
446        stanza = domish.Element((None, "presence"))
447        stanza['from'] = 'other@example.com'
448        self.service.dispatch(self.xmlstream, stanza)
449
450        self.assertEqual(1, len(errors))
Note: See TracBrowser for help on using the repository browser.