pyrad 2.4__py3-none-any.whl → 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. docs/Makefile +20 -0
  2. docs/make.bat +36 -0
  3. docs/source/_static/logo.png +0 -0
  4. docs/source/api/client.rst +10 -0
  5. docs/source/api/dictionary.rst +10 -0
  6. docs/source/api/host.rst +7 -0
  7. docs/source/api/packet.rst +48 -0
  8. docs/source/api/proxy.rst +7 -0
  9. docs/source/api/server.rst +13 -0
  10. docs/source/conf.py +158 -0
  11. docs/source/index.rst +75 -0
  12. example/acct.py +41 -0
  13. example/auth.py +37 -0
  14. example/auth_async.py +164 -0
  15. example/client-coa.py +61 -0
  16. example/coa.py +40 -0
  17. example/dictionary +405 -0
  18. example/dictionary.freeradius +91 -0
  19. example/pyrad.log +0 -0
  20. example/server.py +68 -0
  21. example/server_async.py +117 -0
  22. example/status.py +26 -0
  23. pyrad/__init__.py +3 -3
  24. pyrad/client.py +14 -6
  25. pyrad/client_async.py +16 -13
  26. pyrad/dictfile.py +2 -5
  27. pyrad/dictionary.py +6 -7
  28. pyrad/host.py +1 -1
  29. pyrad/packet.py +145 -114
  30. pyrad/proxy.py +2 -2
  31. pyrad/server.py +3 -7
  32. pyrad/server_async.py +3 -4
  33. pyrad/tests/__init__.py +5 -0
  34. pyrad/tests/mock.py +145 -0
  35. pyrad/tests/test_bidict.py +56 -0
  36. pyrad/tests/test_client.py +183 -0
  37. pyrad/tests/test_dictionary.py +357 -0
  38. pyrad/tests/test_host.py +87 -0
  39. pyrad/tests/test_packet.py +679 -0
  40. pyrad/tests/test_proxy.py +96 -0
  41. pyrad/tests/test_server.py +323 -0
  42. pyrad/tests/test_tools.py +126 -0
  43. pyrad/tools.py +254 -158
  44. {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/METADATA +44 -20
  45. pyrad-2.5.0.dist-info/RECORD +51 -0
  46. {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/WHEEL +1 -1
  47. {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info/licenses}/LICENSE.txt +1 -1
  48. pyrad-2.5.0.dist-info/top_level.txt +3 -0
  49. pyrad-2.4.dist-info/RECORD +0 -19
  50. pyrad-2.4.dist-info/top_level.txt +0 -1
  51. {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,679 @@
1
+ import hmac
2
+ import os
3
+ import struct
4
+ import unittest
5
+
6
+ from . import home
7
+
8
+ from collections import OrderedDict
9
+ from pyrad import packet
10
+ from pyrad.client import Client
11
+ from pyrad.dictionary import Dictionary
12
+ try:
13
+ import hashlib
14
+ md5_constructor = hashlib.md5
15
+ except ImportError:
16
+ # BBB for python 2.4
17
+ import md5
18
+ md5_constructor = md5.new
19
+
20
+
21
+ class UtilityTests(unittest.TestCase):
22
+ def testGenerateID(self):
23
+ id = packet.CreateID()
24
+ self.assertTrue(isinstance(id, int))
25
+ newid = packet.CreateID()
26
+ self.assertNotEqual(id, newid)
27
+
28
+
29
+ class PacketConstructionTests(unittest.TestCase):
30
+ klass = packet.Packet
31
+
32
+ def setUp(self):
33
+ self.path = os.path.join(home, 'data')
34
+ self.dict = Dictionary(os.path.join(self.path, 'simple'))
35
+
36
+ def testBasicConstructor(self):
37
+ pkt = self.klass()
38
+ self.assertTrue(isinstance(pkt.code, int))
39
+ self.assertTrue(isinstance(pkt.id, int))
40
+ self.assertTrue(isinstance(pkt.secret, bytes))
41
+
42
+ def testNamedConstructor(self):
43
+ pkt = self.klass(code=26, id=38, secret=b'secret',
44
+ authenticator=b'authenticator',
45
+ dict='fakedict')
46
+ self.assertEqual(pkt.code, 26)
47
+ self.assertEqual(pkt.id, 38)
48
+ self.assertEqual(pkt.secret, b'secret')
49
+ self.assertEqual(pkt.authenticator, b'authenticator')
50
+ self.assertEqual(pkt.dict, 'fakedict')
51
+
52
+ def testConstructWithDictionary(self):
53
+ pkt = self.klass(dict=self.dict)
54
+ self.assertTrue(pkt.dict is self.dict)
55
+
56
+ def testConstructorIgnoredParameters(self):
57
+ marker = []
58
+ pkt = self.klass(fd=marker)
59
+ self.assertFalse(getattr(pkt, 'fd', None) is marker)
60
+
61
+ def testSecretMustBeBytestring(self):
62
+ self.assertRaises(TypeError, self.klass, secret='secret')
63
+
64
+ def testConstructorWithAttributes(self):
65
+ pkt = self.klass(**{'Test-String': 'this works', 'dict': self.dict})
66
+ self.assertEqual(pkt['Test-String'], ['this works'])
67
+
68
+ def testConstructorWithTlvAttribute(self):
69
+ pkt = self.klass(**{
70
+ 'Test-Tlv-Str': 'this works',
71
+ 'Test-Tlv-Int': 10,
72
+ 'dict': self.dict
73
+ })
74
+ self.assertEqual(
75
+ pkt['Test-Tlv'],
76
+ {'Test-Tlv-Str': ['this works'], 'Test-Tlv-Int': [10]}
77
+ )
78
+
79
+
80
+ class PacketTests(unittest.TestCase):
81
+ def setUp(self):
82
+ self.path = os.path.join(home, 'data')
83
+ self.dict = Dictionary(os.path.join(self.path, 'full'))
84
+ self.packet = packet.Packet(
85
+ id=0, secret=b'secret',
86
+ authenticator=b'01234567890ABCDEF', dict=self.dict)
87
+
88
+ def _create_reply_with_duplicate_attributes(self, request):
89
+ """
90
+ Creates a reply to the given request with multiple instances of the
91
+ same attribute that also do not appear sequentially in the list. Used
92
+ to ensure that methods providing authenticator and
93
+ Message-Authenticator verification can handle the case where multiple
94
+ instances of an given attribute do not appear sequentially in the
95
+ attributes list.
96
+ """
97
+ # Manually build the packet since using packet.Packet will always group
98
+ # attributes of the same type together
99
+ attributes = self._get_attribute_bytes('Test-String', 'test')
100
+ attributes += self._get_attribute_bytes('Test-Integer', 1)
101
+ attributes += self._get_attribute_bytes('Test-String', 'test')
102
+ attributes += self._get_attribute_bytes('Message-Authenticator',
103
+ 16 * b'\00')
104
+
105
+ header = struct.pack('!BBH', packet.AccessAccept,
106
+ request.id, (20 + len(attributes)))
107
+
108
+ # Calculate the Message-Authenticator and update the attribute
109
+ hmac_constructor = hmac.new(request.secret, None, md5_constructor)
110
+ hmac_constructor.update(header + request.authenticator + attributes)
111
+ updated_message_authenticator = hmac_constructor.digest()
112
+ attributes = attributes.replace(b'\x00' * 16,
113
+ updated_message_authenticator)
114
+
115
+ # Calculate the response authenticator
116
+ authenticator = md5_constructor(header
117
+ + request.authenticator
118
+ + attributes
119
+ + request.secret).digest()
120
+
121
+ reply_bytes = header + authenticator + attributes
122
+ return packet.AuthPacket(packet=reply_bytes, dict=self.dict)
123
+
124
+ def _get_attribute_bytes(self, attr_name, value):
125
+ attr = self.dict.attributes[attr_name]
126
+ attr_key = attr.code
127
+ attr_value = packet.tools.EncodeAttr(attr.type, value)
128
+ attr_len = len(attr_value) + 2
129
+ return struct.pack('!BB', attr_key, attr_len) + attr_value
130
+
131
+ def testCreateReply(self):
132
+ reply = self.packet.CreateReply(**{'Test-Integer': 10})
133
+ self.assertEqual(reply.id, self.packet.id)
134
+ self.assertEqual(reply.secret, self.packet.secret)
135
+ self.assertEqual(reply.authenticator, self.packet.authenticator)
136
+ self.assertEqual(reply['Test-Integer'], [10])
137
+
138
+ def testAttributeAccess(self):
139
+ self.packet['Test-Integer'] = 10
140
+ self.assertEqual(self.packet['Test-Integer'], [10])
141
+ self.assertEqual(self.packet[3], [b'\x00\x00\x00\x0a'])
142
+
143
+ self.packet['Test-String'] = 'dummy'
144
+ self.assertEqual(self.packet['Test-String'], ['dummy'])
145
+ self.assertEqual(self.packet[1], [b'dummy'])
146
+
147
+ def testAttributeValueAccess(self):
148
+ self.packet['Test-Integer'] = 'Three'
149
+ self.assertEqual(self.packet['Test-Integer'], ['Three'])
150
+ self.assertEqual(self.packet[3], [b'\x00\x00\x00\x03'])
151
+
152
+ def testVendorAttributeAccess(self):
153
+ self.packet['Simplon-Number'] = 10
154
+ self.assertEqual(self.packet['Simplon-Number'], [10])
155
+ self.assertEqual(self.packet[(16, 1)], [b'\x00\x00\x00\x0a'])
156
+
157
+ self.packet['Simplon-Number'] = 'Four'
158
+ self.assertEqual(self.packet['Simplon-Number'], ['Four'])
159
+ self.assertEqual(self.packet[(16, 1)], [b'\x00\x00\x00\x04'])
160
+
161
+ def testRawAttributeAccess(self):
162
+ marker = [b'']
163
+ self.packet[1] = marker
164
+ self.assertTrue(self.packet[1] is marker)
165
+ self.packet[(16, 1)] = marker
166
+ self.assertTrue(self.packet[(16, 1)] is marker)
167
+
168
+ def testEncryptedAttributes(self):
169
+ self.packet['Test-Encrypted-String'] = 'dummy'
170
+ self.assertEqual(self.packet['Test-Encrypted-String'], ['dummy'])
171
+ self.packet['Test-Encrypted-Integer'] = 10
172
+ self.assertEqual(self.packet['Test-Encrypted-Integer'], [10])
173
+
174
+ def testHasKey(self):
175
+ self.assertEqual(self.packet.has_key('Test-String'), False)
176
+ self.assertEqual('Test-String' in self.packet, False)
177
+ self.packet['Test-String'] = 'dummy'
178
+ self.assertEqual(self.packet.has_key('Test-String'), True)
179
+ self.assertEqual(self.packet.has_key(1), True)
180
+ self.assertEqual(1 in self.packet, True)
181
+
182
+ def testHasKeyWithUnknownKey(self):
183
+ self.assertEqual(self.packet.has_key('Unknown-Attribute'), False)
184
+ self.assertEqual('Unknown-Attribute' in self.packet, False)
185
+
186
+ def testDelItem(self):
187
+ self.packet['Test-String'] = 'dummy'
188
+ del self.packet['Test-String']
189
+ self.assertEqual(self.packet.has_key('Test-String'), False)
190
+ self.packet['Test-String'] = 'dummy'
191
+ del self.packet[1]
192
+ self.assertEqual(self.packet.has_key('Test-String'), False)
193
+
194
+ def testKeys(self):
195
+ self.assertEqual(self.packet.keys(), [])
196
+ self.packet['Test-String'] = 'dummy'
197
+ self.assertEqual(self.packet.keys(), ['Test-String'])
198
+ self.packet['Test-Integer'] = 10
199
+ self.assertEqual(self.packet.keys(), ['Test-String', 'Test-Integer'])
200
+ OrderedDict.__setitem__(self.packet, 12345, None)
201
+ self.assertEqual(self.packet.keys(),
202
+ ['Test-String', 'Test-Integer', 12345])
203
+
204
+ def testCreateAuthenticator(self):
205
+ a = packet.Packet.CreateAuthenticator()
206
+ self.assertTrue(isinstance(a, bytes))
207
+ self.assertEqual(len(a), 16)
208
+
209
+ b = packet.Packet.CreateAuthenticator()
210
+ self.assertNotEqual(a, b)
211
+
212
+ def testGenerateID(self):
213
+ id = self.packet.CreateID()
214
+ self.assertTrue(isinstance(id, int))
215
+ newid = self.packet.CreateID()
216
+ self.assertNotEqual(id, newid)
217
+
218
+ def testReplyPacket(self):
219
+ reply = self.packet.ReplyPacket()
220
+ self.assertEqual(reply,
221
+ (b'\x00\x00\x00\x14\xb0\x5e\x4b\xfb\xcc\x1c'
222
+ b'\x8c\x8e\xc4\x72\xac\xea\x87\x45\x63\xa7'))
223
+
224
+ def testVerifyReply(self):
225
+ reply = self.packet.CreateReply()
226
+ self.assertEqual(self.packet.VerifyReply(reply), True)
227
+
228
+ reply.id += 1
229
+ self.assertEqual(self.packet.VerifyReply(reply), False)
230
+ reply.id = self.packet.id
231
+
232
+ reply.secret = b'different'
233
+ self.assertEqual(self.packet.VerifyReply(reply), False)
234
+ reply.secret = self.packet.secret
235
+
236
+ reply.authenticator = b'X' * 16
237
+ self.assertEqual(self.packet.VerifyReply(reply), False)
238
+ reply.authenticator = self.packet.authenticator
239
+
240
+ def testVerifyReplyDuplicateAttributes(self):
241
+ reply = self._create_reply_with_duplicate_attributes(self.packet)
242
+ self.assertTrue(self.packet.VerifyReply(
243
+ reply=reply,
244
+ rawreply=reply.raw_packet))
245
+
246
+ def testVerifyMessageAuthenticator(self):
247
+ reply = self.packet.CreateReply(**{
248
+ 'Test-String': 'test',
249
+ 'Test-Integer': 3,
250
+ })
251
+ reply.code = packet.AccessAccept
252
+ reply.add_message_authenticator()
253
+ reply._refresh_message_authenticator()
254
+ self.assertTrue(reply.verify_message_authenticator(
255
+ secret=b'secret',
256
+ original_authenticator=self.packet.authenticator,
257
+ original_code=self.packet.code))
258
+
259
+ self.assertFalse(reply.verify_message_authenticator(
260
+ secret=b'bad_secret',
261
+ original_authenticator=self.packet.authenticator,
262
+ original_code=self.packet.code))
263
+
264
+ self.assertFalse(reply.verify_message_authenticator(
265
+ secret=b'secret',
266
+ original_authenticator=b'bad_authenticator',
267
+ original_code=self.packet.code))
268
+
269
+ def testVerifyMessageAuthenticatorDuplicateAttributes(self):
270
+ reply = self._create_reply_with_duplicate_attributes(self.packet)
271
+ self.assertTrue(reply.verify_message_authenticator(
272
+ secret=b'secret',
273
+ original_authenticator=self.packet.authenticator,
274
+ original_code=packet.AccessRequest))
275
+
276
+ def testPktEncodeAttribute(self):
277
+ encode = self.packet._PktEncodeAttribute
278
+
279
+ # Encode a normal attribute
280
+ self.assertEqual(
281
+ encode(1, b'value'),
282
+ b'\x01\x07value')
283
+ # Encode a vendor attribute
284
+ self.assertEqual(
285
+ encode((1, 2), b'value'),
286
+ b'\x1a\x0d\x00\x00\x00\x01\x02\x07value')
287
+
288
+ def testPktEncodeTlvAttribute(self):
289
+ encode = self.packet._PktEncodeTlv
290
+
291
+ # Encode a normal tlv attribute
292
+ self.assertEqual(
293
+ encode(4, {1: [b'value'], 2: [b'\x00\x00\x00\x02']}),
294
+ b'\x04\x0f\x01\x07value\x02\x06\x00\x00\x00\x02')
295
+
296
+ # Encode a normal tlv attribute with several sub attribute instances
297
+ self.assertEqual(
298
+ encode(4, {1: [b'value', b'other'], 2: [b'\x00\x00\x00\x02']}),
299
+ b'\x04\x16\x01\x07value\x02\x06\x00\x00\x00\x02\x01\x07other')
300
+ # Encode a vendor tlv attribute
301
+ self.assertEqual(
302
+ encode((16, 3), {1: [b'value'], 2: [b'\x00\x00\x00\x02']}),
303
+ b'\x1a\x15\x00\x00\x00\x10\x03\x0f\x01\x07value\x02\x06\x00\x00\x00\x02')
304
+
305
+ def testPktEncodeLongTlvAttribute(self):
306
+ encode = self.packet._PktEncodeTlv
307
+
308
+ long_str = b'a' * 245
309
+ # Encode a long tlv attribute - check it is split between AVPs
310
+ self.assertEqual(
311
+ encode(4, {1: [b'value', long_str], 2: [b'\x00\x00\x00\x02']}),
312
+ b'\x04\x0f\x01\x07value\x02\x06\x00\x00\x00\x02\x04\xf9\x01\xf7' + long_str)
313
+
314
+ # Encode a long vendor tlv attribute
315
+ first_avp = b'\x1a\x15\x00\x00\x00\x10\x03\x0f\x01\x07value\x02\x06\x00\x00\x00\x02'
316
+ second_avp = b'\x1a\xff\x00\x00\x00\x10\x03\xf9\x01\xf7' + long_str
317
+ self.assertEqual(
318
+ encode((16, 3), {1: [b'value', long_str], 2: [b'\x00\x00\x00\x02']}),
319
+ first_avp + second_avp)
320
+
321
+ def testPktEncodeAttributes(self):
322
+ self.packet[1] = [b'value']
323
+ self.assertEqual(self.packet._PktEncodeAttributes(),
324
+ b'\x01\x07value')
325
+
326
+ self.packet.clear()
327
+ self.packet[(16, 2)] = [b'value']
328
+ self.assertEqual(self.packet._PktEncodeAttributes(),
329
+ b'\x1a\x0d\x00\x00\x00\x10\x02\x07value')
330
+
331
+ self.packet.clear()
332
+ self.packet[1] = [b'one', b'two', b'three']
333
+ self.assertEqual(self.packet._PktEncodeAttributes(),
334
+ b'\x01\x05one\x01\x05two\x01\x07three')
335
+
336
+ self.packet.clear()
337
+ self.packet[1] = [b'value']
338
+ self.packet[(16, 2)] = [b'value']
339
+ self.assertEqual(
340
+ self.packet._PktEncodeAttributes(),
341
+ b'\x01\x07value\x1a\x0d\x00\x00\x00\x10\x02\x07value')
342
+
343
+ def testPktDecodeVendorAttribute(self):
344
+ decode = self.packet._PktDecodeVendorAttribute
345
+
346
+ # Non-RFC2865 recommended form
347
+ self.assertEqual(decode(b''), [(26, b'')])
348
+ self.assertEqual(decode(b'12345'), [(26, b'12345')])
349
+
350
+ # Almost RFC2865 recommended form: bad length value
351
+ self.assertEqual(
352
+ decode(b'\x00\x00\x00\x01\x02\x06value'),
353
+ [(26, b'\x00\x00\x00\x01\x02\x06value')])
354
+
355
+ # Proper RFC2865 recommended form
356
+ self.assertEqual(
357
+ decode(b'\x00\x00\x00\x10\x02\x07value'),
358
+ [((16, 2), b'value')])
359
+
360
+ def testPktDecodeTlvAttribute(self):
361
+ decode = self.packet._PktDecodeTlvAttribute
362
+
363
+ decode(4, b'\x01\x07value')
364
+ self.assertEqual(self.packet[4], {1: [b'value']})
365
+
366
+ # add another instance of the same sub attribute
367
+ decode(4, b'\x01\x07other')
368
+ self.assertEqual(self.packet[4], {1: [b'value', b'other']})
369
+
370
+ # add a different sub attribute
371
+ decode(4, b'\x02\x07\x00\x00\x00\x01')
372
+ self.assertEqual(self.packet[4], {
373
+ 1: [b'value', b'other'],
374
+ 2: [b'\x00\x00\x00\x01']
375
+ })
376
+
377
+ def testDecodePacketWithEmptyPacket(self):
378
+ try:
379
+ self.packet.DecodePacket(b'')
380
+ except packet.PacketError as e:
381
+ self.assertTrue('header is corrupt' in str(e))
382
+ else:
383
+ self.fail()
384
+
385
+ def testDecodePacketWithInvalidLength(self):
386
+ try:
387
+ self.packet.DecodePacket(b'\x00\x00\x00\x001234567890123456')
388
+ except packet.PacketError as e:
389
+ self.assertTrue('invalid length' in str(e))
390
+ else:
391
+ self.fail()
392
+
393
+ def testDecodePacketWithTooBigPacket(self):
394
+ try:
395
+ self.packet.DecodePacket(b'\x00\x00\x24\x00' + (0x2400 - 4) * b'X')
396
+ except packet.PacketError as e:
397
+ self.assertTrue('too long' in str(e))
398
+ else:
399
+ self.fail()
400
+
401
+ def testDecodePacketWithPartialAttributes(self):
402
+ try:
403
+ self.packet.DecodePacket(
404
+ b'\x01\x02\x00\x151234567890123456\x00')
405
+ except packet.PacketError as e:
406
+ self.assertTrue('header is corrupt' in str(e))
407
+ else:
408
+ self.fail()
409
+
410
+ def testDecodePacketWithoutAttributes(self):
411
+ self.packet.DecodePacket(b'\x01\x02\x00\x141234567890123456')
412
+ self.assertEqual(self.packet.code, 1)
413
+ self.assertEqual(self.packet.id, 2)
414
+ self.assertEqual(self.packet.authenticator, b'1234567890123456')
415
+ self.assertEqual(self.packet.keys(), [])
416
+
417
+ def testDecodePacketWithBadAttribute(self):
418
+ try:
419
+ self.packet.DecodePacket(
420
+ b'\x01\x02\x00\x161234567890123456\x00\x01')
421
+ except packet.PacketError as e:
422
+ self.assertTrue('too small' in str(e))
423
+ else:
424
+ self.fail()
425
+
426
+ def testDecodePacketWithEmptyAttribute(self):
427
+ self.packet.DecodePacket(
428
+ b'\x01\x02\x00\x161234567890123456\x01\x02')
429
+ self.assertEqual(self.packet[1], [b''])
430
+
431
+ def testDecodePacketWithAttribute(self):
432
+ self.packet.DecodePacket(
433
+ b'\x01\x02\x00\x1b1234567890123456\x01\x07value')
434
+ self.assertEqual(self.packet[1], [b'value'])
435
+
436
+ def testDecodePacketWithTlvAttribute(self):
437
+ self.packet.DecodePacket(
438
+ b'\x01\x02\x00\x1d1234567890123456\x04\x09\x01\x07value')
439
+ self.assertEqual(self.packet[4], {1: [b'value']})
440
+
441
+ def testDecodePacketWithVendorTlvAttribute(self):
442
+ self.packet.DecodePacket(
443
+ b'\x01\x02\x00\x231234567890123456\x1a\x0f\x00\x00\x00\x10\x03\x09\x01\x07value')
444
+ self.assertEqual(self.packet[(16, 3)], {1: [b'value']})
445
+
446
+ def testDecodePacketWithTlvAttributeWith2SubAttributes(self):
447
+ self.packet.DecodePacket(
448
+ b'\x01\x02\x00\x231234567890123456\x04\x0f\x01\x07value\x02\x06\x00\x00\x00\x09')
449
+ self.assertEqual(self.packet[4], {1: [b'value'], 2: [b'\x00\x00\x00\x09']})
450
+
451
+ def testDecodePacketWithSplitTlvAttribute(self):
452
+ self.packet.DecodePacket(
453
+ b'\x01\x02\x00\x251234567890123456\x04\x09\x01\x07value\x04\x09\x02\x06\x00\x00\x00\x09')
454
+ self.assertEqual(self.packet[4], {1: [b'value'], 2: [b'\x00\x00\x00\x09']})
455
+
456
+ def testDecodePacketWithMultiValuedAttribute(self):
457
+ self.packet.DecodePacket(
458
+ b'\x01\x02\x00\x1e1234567890123456\x01\x05one\x01\x05two')
459
+ self.assertEqual(self.packet[1], [b'one', b'two'])
460
+
461
+ def testDecodePacketWithTwoAttributes(self):
462
+ self.packet.DecodePacket(
463
+ b'\x01\x02\x00\x1e1234567890123456\x01\x05one\x01\x05two')
464
+ self.assertEqual(self.packet[1], [b'one', b'two'])
465
+
466
+ def testDecodePacketWithVendorAttribute(self):
467
+ self.packet.DecodePacket(
468
+ b'\x01\x02\x00\x1b1234567890123456\x1a\x07value')
469
+ self.assertEqual(self.packet[26], [b'value'])
470
+
471
+ def testEncodeKeyValues(self):
472
+ self.assertEqual(self.packet._EncodeKeyValues(1, '1234'), (1, '1234'))
473
+
474
+ def testEncodeKey(self):
475
+ self.assertEqual(self.packet._EncodeKey(1), 1)
476
+
477
+ def testAddAttribute(self):
478
+ self.packet.AddAttribute('Test-String', '1')
479
+ self.assertEqual(self.packet['Test-String'], ['1'])
480
+ self.packet.AddAttribute('Test-String', '1')
481
+ self.assertEqual(self.packet['Test-String'], ['1', '1'])
482
+ self.packet.AddAttribute('Test-String', ['2', '3'])
483
+ self.assertEqual(self.packet['Test-String'], ['1', '1', '2', '3'])
484
+
485
+
486
+ class AuthPacketConstructionTests(PacketConstructionTests):
487
+ klass = packet.AuthPacket
488
+
489
+ def testConstructorDefaults(self):
490
+ pkt = self.klass()
491
+ self.assertEqual(pkt.code, packet.AccessRequest)
492
+
493
+
494
+ class AuthPacketTests(unittest.TestCase):
495
+ def setUp(self):
496
+ self.path = os.path.join(home, 'data')
497
+ self.dict = Dictionary(os.path.join(self.path, 'full'))
498
+ self.packet = packet.AuthPacket(id=0, secret=b'secret',
499
+ authenticator=b'01234567890ABCDEF',
500
+ dict=self.dict)
501
+
502
+ def testCreateReply(self):
503
+ reply = self.packet.CreateReply(**{'Test-Integer': 10})
504
+ self.assertEqual(reply.code, packet.AccessAccept)
505
+ self.assertEqual(reply.id, self.packet.id)
506
+ self.assertEqual(reply.secret, self.packet.secret)
507
+ self.assertEqual(reply.authenticator, self.packet.authenticator)
508
+ self.assertEqual(reply['Test-Integer'], [10])
509
+
510
+ def testRequestPacket(self):
511
+ self.assertEqual(self.packet.RequestPacket(),
512
+ b'\x01\x00\x00\x1401234567890ABCDE')
513
+
514
+ def testRequestPacketCreatesAuthenticator(self):
515
+ self.packet.authenticator = None
516
+ self.packet.RequestPacket()
517
+ self.assertTrue(self.packet.authenticator is not None)
518
+
519
+ def testRequestPacketCreatesID(self):
520
+ self.packet.id = None
521
+ self.packet.RequestPacket()
522
+ self.assertTrue(self.packet.id is not None)
523
+
524
+ def testPwCryptEmptyPassword(self):
525
+ self.assertEqual(self.packet.PwCrypt(''), b'')
526
+
527
+ def testPwCryptPassword(self):
528
+ self.assertEqual(self.packet.PwCrypt('Simplon'),
529
+ b'\xd3U;\xb23\r\x11\xba\x07\xe3\xa8*\xa8x\x14\x01')
530
+
531
+ def testPwCryptSetsAuthenticator(self):
532
+ self.packet.authenticator = None
533
+ self.packet.PwCrypt('')
534
+ self.assertTrue(self.packet.authenticator is not None)
535
+
536
+ def testPwDecryptEmptyPassword(self):
537
+ self.assertEqual(self.packet.PwDecrypt(b''), '')
538
+
539
+ def testPwDecryptPassword(self):
540
+ self.assertEqual(self.packet.PwDecrypt(
541
+ b'\xd3U;\xb23\r\x11\xba\x07\xe3\xa8*\xa8x\x14\x01'),
542
+ 'Simplon')
543
+
544
+
545
+ class AuthPacketChapTests(unittest.TestCase):
546
+ def setUp(self):
547
+ self.path = os.path.join(home, 'data')
548
+ self.dict = Dictionary(os.path.join(self.path, 'chap'))
549
+ # self.packet = packet.Packet(id=0, secret=b'secret',
550
+ # dict=self.dict)
551
+ self.client = Client(server='localhost', secret=b'secret',
552
+ dict=self.dict)
553
+
554
+ def testVerifyChapPasswd(self):
555
+ chap_id = b'9'
556
+ chap_challenge = b'987654321'
557
+ chap_password = chap_id + md5_constructor(
558
+ chap_id + b'test_password' + chap_challenge).digest()
559
+ pkt = self.client.CreateAuthPacket(
560
+ code=packet.AccessChallenge,
561
+ authenticator=b'ABCDEFG',
562
+ User_Name='test_name',
563
+ CHAP_Challenge=chap_challenge,
564
+ CHAP_Password=chap_password
565
+ )
566
+ self.assertEqual(pkt['CHAP-Challenge'][0], chap_challenge)
567
+ self.assertEqual(pkt['CHAP-Password'][0], chap_password)
568
+ self.assertEqual(pkt.VerifyChapPasswd('test_password'), True)
569
+
570
+
571
+ class AcctPacketConstructionTests(PacketConstructionTests):
572
+ klass = packet.AcctPacket
573
+
574
+ def testConstructorDefaults(self):
575
+ pkt = self.klass()
576
+ self.assertEqual(pkt.code, packet.AccountingRequest)
577
+
578
+ def testConstructorRawPacket(self):
579
+ raw = (b'\x00\x00\x00\x14\xb0\x5e\x4b\xfb\xcc\x1c'
580
+ b'\x8c\x8e\xc4\x72\xac\xea\x87\x45\x63\xa7')
581
+ pkt = self.klass(packet=raw)
582
+ self.assertEqual(pkt.raw_packet, raw)
583
+
584
+
585
+ class AcctPacketTests(unittest.TestCase):
586
+ def setUp(self):
587
+ self.path = os.path.join(home, 'data')
588
+ self.dict = Dictionary(os.path.join(self.path, 'full'))
589
+ self.packet = packet.AcctPacket(id=0, secret=b'secret',
590
+ authenticator=b'01234567890ABCDEF',
591
+ dict=self.dict)
592
+
593
+ def testCreateReply(self):
594
+ reply = self.packet.CreateReply(**{'Test-Integer': 10})
595
+ self.assertEqual(reply.code, packet.AccountingResponse)
596
+ self.assertEqual(reply.id, self.packet.id)
597
+ self.assertEqual(reply.secret, self.packet.secret)
598
+ self.assertEqual(reply.authenticator, self.packet.authenticator)
599
+ self.assertEqual(reply['Test-Integer'], [10])
600
+
601
+ def testVerifyAcctRequest(self):
602
+ rawpacket = self.packet.RequestPacket()
603
+ pkt = packet.AcctPacket(secret=b'secret', packet=rawpacket)
604
+ self.assertEqual(pkt.VerifyAcctRequest(), True)
605
+
606
+ pkt.secret = b'different'
607
+ self.assertEqual(pkt.VerifyAcctRequest(), False)
608
+ pkt.secret = b'secret'
609
+
610
+ pkt.raw_packet = b'X' + pkt.raw_packet[1:]
611
+ self.assertEqual(pkt.VerifyAcctRequest(), False)
612
+
613
+ def testRequestPacket(self):
614
+ self.assertEqual(self.packet.RequestPacket(),
615
+ b'\x04\x00\x00\x14\x95\xdf\x90\xccbn\xfb\x15G!\x13\xea\xfa>6\x0f')
616
+
617
+ def testRequestPacketSetsId(self):
618
+ self.packet.id = None
619
+ self.packet.RequestPacket()
620
+ self.assertTrue(self.packet.id is not None)
621
+
622
+ def testRealisticUnknownAttributes(self):
623
+ """ Test a realistic Accounting Packet from raw
624
+ User-Name: [u'user@example.com']
625
+ NAS-IP-Address: ['1.2.3.4']
626
+ Service-Type: ['Framed-User']
627
+ Framed-Protocol: ['NAS-Prompt-User']
628
+ Framed-IP-Address: ['1.2.3.4']
629
+ Acct-Status-Type: ['Interim-Update']
630
+ Acct-Delay-Time: [0]
631
+ Acct-Input-Octets: [1290826858]
632
+ Acct-Output-Octets: [3551101035]
633
+ Acct-Session-Id: [u'90dbd65a18b0a6c']
634
+ Acct-Authentic: ['RADIUS']
635
+ Acct-Session-Time: [769500]
636
+ Acct-Input-Packets: [7403861]
637
+ Acct-Output-Packets: [10928170]
638
+ Acct-Link-Count: [1]
639
+ Acct-Input-Gigawords: [0]
640
+ Acct-Output-Gigawords: [2]
641
+ Event-Timestamp: [1554155989]
642
+ # vendor specific
643
+ NAS-Port-Type: ['Virtual']
644
+ (26, 594, 1): [u'UNKNOWN_PRODUCT']
645
+ # implementation specific fields
646
+ 224: ['24P\x10\x00\x22\x96\xc9']
647
+ 228: ['\xfe\x99\xd0P']
648
+ """
649
+ # path = os.path.join(home, 'tests', 'data')
650
+ path = os.path.join(home, 'data')
651
+ dictObj = Dictionary(os.path.join(path, 'realistic'))
652
+ raw = b'\x04\x8e\x00\xc4\xb2\xf8z\xdb\xac\xfd9l\x9dI?E\x8c%\xe9'\
653
+ b'\xf5\x01\x12user@example.com\x04\x06\x01\x02\x03\x04\x06\x06'\
654
+ b'\x00\x00\x00\x02\x07\x06\x00\x00\x00\x07\x08\x06\x01\x02\x03'\
655
+ b'\x04(\x06\x00\x00\x00\x03)\x06\x00\x00\x00\x00*\x06L\xf0tj+'\
656
+ b'\x06\xd3\xa9\x80k,\x1190dbd65a18b0a6c-\x06\x00\x00\x00\x01.'\
657
+ b'\x06\x00\x0b\xbd\xdc/\x06\x00p\xf9U0\x06\x00\xa6\xc0*3\x06'\
658
+ b'\x00\x00\x00\x014\x06\x00\x00\x00\x005\x06\x00\x00\x00\x027'\
659
+ b'\x06\\\xa2\x89\xd5=\x06\x00\x00\x00\x05\x1a\x17\x00\x00\x02R'\
660
+ b'\x01\x11UNKNOWN_PRODUCT\xe0\n24P\x10\x00\x22\x96\xc9\xe4\x06'\
661
+ b'\xfe\x99\xd0P'
662
+
663
+ pkt = packet.AcctPacket(dict=dictObj, packet=raw)
664
+ self.assertEqual(pkt.raw_packet, raw)
665
+
666
+ # Test verifies packet parses correctly
667
+ self.assertEqual(pkt.code, packet.AccountingRequest) # 4
668
+ self.assertEqual(pkt['User-Name'], ['user@example.com'])
669
+ self.assertEqual(pkt['NAS-IP-Address'], ['1.2.3.4'])
670
+ self.assertEqual(pkt['Acct-Status-Type'], ['Interim-Update'])
671
+ self.assertEqual(pkt['Acct-Session-Id'], ['90dbd65a18b0a6c'])
672
+ self.assertEqual(pkt['Acct-Authentic'], ['RADIUS'])
673
+
674
+ # Unknown attributes preserved
675
+ self.assertEqual(pkt[224][0], b'24P\x10\x00\x22\x96\xc9')
676
+ self.assertEqual(pkt[228][0], b'\xfe\x99\xd0P')
677
+
678
+ # Vendor unknown preserved
679
+ self.assertEqual(pkt[(594, 1)], [b'UNKNOWN_PRODUCT'])