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
pyrad/server_async.py CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  import asyncio
6
6
  import logging
7
- import traceback
8
7
 
9
8
  from abc import abstractmethod, ABCMeta
10
9
  from enum import Enum
@@ -81,7 +80,7 @@ class DatagramProtocolServer(asyncio.Protocol):
81
80
  dict=self.server.dict,
82
81
  packet=data)
83
82
  if self.server.enable_pkt_verify:
84
- if req.VerifyAuthRequest():
83
+ if not req.VerifyAuthRequest():
85
84
  raise PacketError('Packet verification failed')
86
85
 
87
86
  elif self.server_type == ServerType.Coa:
@@ -91,7 +90,7 @@ class DatagramProtocolServer(asyncio.Protocol):
91
90
  dict=self.server.dict,
92
91
  packet=data)
93
92
  if self.server.enable_pkt_verify:
94
- if req.VerifyCoARequest():
93
+ if not req.VerifyCoARequest():
95
94
  raise PacketError('Packet verification failed')
96
95
 
97
96
  elif self.server_type == ServerType.Acct:
@@ -102,7 +101,7 @@ class DatagramProtocolServer(asyncio.Protocol):
102
101
  dict=self.server.dict,
103
102
  packet=data)
104
103
  if self.server.enable_pkt_verify:
105
- if req.VerifyAcctRequest():
104
+ if not req.VerifyAcctRequest():
106
105
  raise PacketError('Packet verification failed')
107
106
 
108
107
  # Call request callback
@@ -0,0 +1,5 @@
1
+ import pyrad
2
+ import os
3
+
4
+ pyrad # keep pyflakes happy
5
+ home = os.path.dirname(os.path.realpath(__file__))
pyrad/tests/mock.py ADDED
@@ -0,0 +1,145 @@
1
+ import fcntl
2
+ import os
3
+ from pyrad.packet import PacketError
4
+
5
+
6
+ class MockPacket:
7
+ reply = object()
8
+
9
+ def __init__(self, code, verify=False, error=False):
10
+ self.code = code
11
+ self.data = {}
12
+ self.verify = verify
13
+ self.error = error
14
+
15
+ def CreateReply(self, packet=None):
16
+ if self.error:
17
+ raise PacketError
18
+ return self.reply
19
+
20
+ def VerifyReply(self, reply, rawreply):
21
+ return self.verify
22
+
23
+ def RequestPacket(self):
24
+ return "request packet"
25
+
26
+ def __contains__(self, key):
27
+ return key in self.data
28
+ has_key = __contains__
29
+
30
+ def __setitem__(self, key, value):
31
+ self.data[key] = [value]
32
+
33
+ def __getitem__(self, key):
34
+ return self.data[key]
35
+
36
+
37
+ class MockSocket:
38
+ def __init__(self, domain, type, data=None):
39
+ self.domain = domain
40
+ self.type = type
41
+ self.closed = False
42
+ self.options = []
43
+ self.address = None
44
+ self.output = []
45
+
46
+ # Always initialize data so recv() never fails.
47
+ # If data is provided, use it; otherwise behave like "no data available".
48
+ self.data = data if data is not None else b""
49
+
50
+ if data is not None:
51
+ (self.read_end, self.write_end) = os.pipe()
52
+ fcntl.fcntl(self.write_end, fcntl.F_SETFL, os.O_NONBLOCK)
53
+ os.write(self.write_end, data)
54
+ else:
55
+ self.read_end = 1
56
+ self.write_end = None
57
+
58
+ def fileno(self):
59
+ return self.read_end
60
+
61
+ def bind(self, address):
62
+ self.address = address
63
+
64
+ def recv(self, buffer):
65
+ # Return up to `buffer` bytes; if empty, return b"" (no data).
66
+ return self.data[:buffer]
67
+
68
+ def sendto(self, data, target):
69
+ self.output.append((data, target))
70
+
71
+ def setsockopt(self, level, opt, value):
72
+ self.options.append((level, opt, value))
73
+
74
+ def close(self):
75
+ self.closed = True
76
+
77
+
78
+ class MockFinished(Exception):
79
+ pass
80
+
81
+
82
+ class MockPoll:
83
+ results = []
84
+
85
+ def __init__(self):
86
+ self.registry = {}
87
+
88
+ def register(self, fd, options):
89
+ self.registry[fd] = options
90
+
91
+ def unregister(self, fd):
92
+ try:
93
+ del self.registry[fd]
94
+ except KeyError:
95
+ pass
96
+
97
+ def poll(self, timeout=None):
98
+ for result in self.results:
99
+ yield result
100
+ raise MockFinished
101
+
102
+
103
+ def origkey(klass):
104
+ return "_originals_" + klass.__name__
105
+
106
+
107
+ def MockClassMethod(klass, name, myfunc=None):
108
+ def func(self, *args, **kwargs):
109
+ if not hasattr(self, "called"):
110
+ self.called = []
111
+ self.called.append((name, args, kwargs))
112
+
113
+ key = origkey(klass)
114
+ if not hasattr(klass, key):
115
+ setattr(klass, key, {})
116
+ getattr(klass, key)[name] = getattr(klass, name)
117
+ if myfunc is None:
118
+ setattr(klass, name, func)
119
+ else:
120
+ setattr(klass, name, myfunc)
121
+
122
+
123
+ def UnmockClassMethods(klass):
124
+ key = origkey(klass)
125
+ if not hasattr(klass, key):
126
+ return
127
+ for (name, func) in getattr(klass, key).items():
128
+ setattr(klass, name, func)
129
+
130
+ delattr(klass, key)
131
+
132
+
133
+ class MockFd:
134
+ data = object()
135
+ source = object()
136
+
137
+ def __init__(self, fd=0):
138
+ self.fd = fd
139
+
140
+ def fileno(self):
141
+ return self.fd
142
+
143
+ def recvfrom(self, size):
144
+ self.size = size
145
+ return (self.data, self.source)
@@ -0,0 +1,56 @@
1
+ import operator
2
+ import unittest
3
+ from pyrad.bidict import BiDict
4
+
5
+
6
+ class BiDictTests(unittest.TestCase):
7
+ def setUp(self):
8
+ self.bidict = BiDict()
9
+
10
+ def testStartEmpty(self):
11
+ self.assertEqual(len(self.bidict), 0)
12
+ self.assertEqual(len(self.bidict.forward), 0)
13
+ self.assertEqual(len(self.bidict.backward), 0)
14
+
15
+ def testLength(self):
16
+ self.assertEqual(len(self.bidict), 0)
17
+ self.bidict.Add("from", "to")
18
+ self.assertEqual(len(self.bidict), 1)
19
+ del self.bidict["from"]
20
+ self.assertEqual(len(self.bidict), 0)
21
+
22
+ def testDeletion(self):
23
+ self.assertRaises(KeyError, operator.delitem, self.bidict, "missing")
24
+ self.bidict.Add("missing", "present")
25
+ del self.bidict["missing"]
26
+
27
+ def testBackwardDeletion(self):
28
+ self.assertRaises(KeyError, operator.delitem, self.bidict, "missing")
29
+ self.bidict.Add("missing", "present")
30
+ del self.bidict["present"]
31
+ self.assertEqual(self.bidict.HasForward("missing"), False)
32
+
33
+ def testForwardAccess(self):
34
+ self.bidict.Add("shake", "vanilla")
35
+ self.bidict.Add("pie", "custard")
36
+ self.assertEqual(self.bidict.HasForward("shake"), True)
37
+ self.assertEqual(self.bidict.GetForward("shake"), "vanilla")
38
+ self.assertEqual(self.bidict.HasForward("pie"), True)
39
+ self.assertEqual(self.bidict.GetForward("pie"), "custard")
40
+ self.assertEqual(self.bidict.HasForward("missing"), False)
41
+ self.assertRaises(KeyError, self.bidict.GetForward, "missing")
42
+
43
+ def testBackwardAccess(self):
44
+ self.bidict.Add("shake", "vanilla")
45
+ self.bidict.Add("pie", "custard")
46
+ self.assertEqual(self.bidict.HasBackward("vanilla"), True)
47
+ self.assertEqual(self.bidict.GetBackward("vanilla"), "shake")
48
+ self.assertEqual(self.bidict.HasBackward("missing"), False)
49
+ self.assertRaises(KeyError, self.bidict.GetBackward, "missing")
50
+
51
+ def testItemAccessor(self):
52
+ self.bidict.Add("shake", "vanilla")
53
+ self.bidict.Add("pie", "custard")
54
+ self.assertRaises(KeyError, operator.getitem, self.bidict, "missing")
55
+ self.assertEqual(self.bidict["shake"], "vanilla")
56
+ self.assertEqual(self.bidict["pie"], "custard")
@@ -0,0 +1,183 @@
1
+ import select
2
+ import socket
3
+ import unittest
4
+ from .mock import MockPacket
5
+ from .mock import MockPoll
6
+ from .mock import MockSocket
7
+ from pyrad.client import Client
8
+ from pyrad.client import Timeout
9
+ from pyrad.packet import AuthPacket
10
+ from pyrad.packet import AcctPacket
11
+ from pyrad.packet import AccessRequest
12
+ from pyrad.packet import AccountingRequest
13
+
14
+ BIND_IP = "127.0.0.1"
15
+ BIND_PORT = 53535
16
+
17
+
18
+ class ConstructionTests(unittest.TestCase):
19
+ def setUp(self):
20
+ self.server = object()
21
+
22
+ def testSimpleConstruction(self):
23
+ client = Client(self.server)
24
+ self.assertTrue(client.server is self.server)
25
+ self.assertEqual(client.authport, 1812)
26
+ self.assertEqual(client.acctport, 1813)
27
+ self.assertEqual(client.secret, b'')
28
+ self.assertEqual(client.retries, 3)
29
+ self.assertEqual(client.timeout, 5)
30
+ self.assertTrue(client.dict is None)
31
+
32
+ def testParameterOrder(self):
33
+ marker = object()
34
+ client = Client(self.server, 123, 456, 789, "secret", marker)
35
+ self.assertTrue(client.server is self.server)
36
+ self.assertEqual(client.authport, 123)
37
+ self.assertEqual(client.acctport, 456)
38
+ self.assertEqual(client.coaport, 789)
39
+ self.assertEqual(client.secret, "secret")
40
+ self.assertTrue(client.dict is marker)
41
+
42
+ def testNamedParameters(self):
43
+ marker = object()
44
+ client = Client(server=self.server, authport=123, acctport=456,
45
+ secret="secret", dict=marker)
46
+ self.assertTrue(client.server is self.server)
47
+ self.assertEqual(client.authport, 123)
48
+ self.assertEqual(client.acctport, 456)
49
+ self.assertEqual(client.secret, "secret")
50
+ self.assertTrue(client.dict is marker)
51
+
52
+
53
+ class SocketTests(unittest.TestCase):
54
+ def setUp(self):
55
+ self.server = object()
56
+ self.client = Client(self.server)
57
+ self.orgsocket = socket.socket
58
+ socket.socket = MockSocket
59
+
60
+ def tearDown(self):
61
+ socket.socket = self.orgsocket
62
+
63
+ def testReopen(self):
64
+ self.client._SocketOpen()
65
+ sock = self.client._socket
66
+ self.client._SocketOpen()
67
+ self.assertTrue(sock is self.client._socket)
68
+
69
+ def testBind(self):
70
+ self.client.bind((BIND_IP, BIND_PORT))
71
+ self.assertEqual(self.client._socket.address, (BIND_IP, BIND_PORT))
72
+ self.assertEqual(self.client._socket.options,
73
+ [(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)])
74
+
75
+ def testBindClosesSocket(self):
76
+ s = MockSocket(socket.AF_INET, socket.SOCK_DGRAM)
77
+ self.client._socket = s
78
+ self.client._poll = MockPoll()
79
+ self.client.bind((BIND_IP, BIND_PORT))
80
+ self.assertEqual(s.closed, True)
81
+
82
+ def testSendPacket(self):
83
+ def MockSend(self, pkt, port):
84
+ self._mock_pkt = pkt
85
+ self._mock_port = port
86
+
87
+ _SendPacket = Client._SendPacket
88
+ Client._SendPacket = MockSend
89
+
90
+ self.client.SendPacket(AuthPacket())
91
+ self.assertEqual(self.client._mock_port, self.client.authport)
92
+
93
+ self.client.SendPacket(AcctPacket())
94
+ self.assertEqual(self.client._mock_port, self.client.acctport)
95
+
96
+ Client._SendPacket = _SendPacket
97
+
98
+ def testNoRetries(self):
99
+ self.client.retries = 0
100
+ self.assertRaises(Timeout, self.client._SendPacket, None, None)
101
+
102
+ def testSingleRetry(self):
103
+ self.client.retries = 1
104
+ self.client.timeout = 0
105
+ packet = MockPacket(AccessRequest)
106
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
107
+ self.assertEqual(self.client._socket.output,
108
+ [("request packet", (self.server, 432))])
109
+
110
+ def testDoubleRetry(self):
111
+ self.client.retries = 2
112
+ self.client.timeout = 0
113
+ packet = MockPacket(AccessRequest)
114
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
115
+ self.assertEqual(self.client._socket.output,
116
+ [("request packet", (self.server, 432)),
117
+ ("request packet", (self.server, 432))])
118
+
119
+ def testAuthDelay(self):
120
+ self.client.retries = 2
121
+ self.client.timeout = 1
122
+ packet = MockPacket(AccessRequest)
123
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
124
+ self.assertFalse("Acct-Delay-Time" in packet)
125
+
126
+ def testSingleAccountDelay(self):
127
+ self.client.retries = 2
128
+ self.client.timeout = 1
129
+ packet = MockPacket(AccountingRequest)
130
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
131
+ self.assertEqual(packet["Acct-Delay-Time"], [1])
132
+
133
+ def testDoubleAccountDelay(self):
134
+ self.client.retries = 3
135
+ self.client.timeout = 1
136
+ packet = MockPacket(AccountingRequest)
137
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
138
+ self.assertEqual(packet["Acct-Delay-Time"], [2])
139
+
140
+ def testIgnorePacketError(self):
141
+ self.client.retries = 1
142
+ self.client.timeout = 1
143
+ self.client._socket = MockSocket(1, 2, b'valid reply')
144
+ packet = MockPacket(AccountingRequest, verify=True, error=True)
145
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
146
+
147
+ def testValidReply(self):
148
+ self.client.retries = 1
149
+ self.client.timeout = 1
150
+ self.client._socket = MockSocket(1, 2, b'valid reply')
151
+ self.client._poll = MockPoll()
152
+ MockPoll.results = [(1, select.POLLIN)]
153
+ packet = MockPacket(AccountingRequest, verify=True)
154
+ reply = self.client._SendPacket(packet, 432)
155
+ self.assertTrue(reply is packet.reply)
156
+
157
+ def testInvalidReply(self):
158
+ self.client.retries = 1
159
+ self.client.timeout = 1
160
+ self.client._socket = MockSocket(1, 2, b'invalid reply')
161
+ MockPoll.results = [(1, select.POLLIN)]
162
+ packet = MockPacket(AccountingRequest, verify=False)
163
+ self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
164
+
165
+
166
+ class OtherTests(unittest.TestCase):
167
+ def setUp(self):
168
+ self.server = object()
169
+ self.client = Client(self.server, secret=b'zeer geheim')
170
+
171
+ def testCreateAuthPacket(self):
172
+ packet = self.client.CreateAuthPacket(id=15)
173
+ self.assertTrue(isinstance(packet, AuthPacket))
174
+ self.assertTrue(packet.dict is self.client.dict)
175
+ self.assertEqual(packet.id, 15)
176
+ self.assertEqual(packet.secret, b'zeer geheim')
177
+
178
+ def testCreateAcctPacket(self):
179
+ packet = self.client.CreateAcctPacket(id=15)
180
+ self.assertTrue(isinstance(packet, AcctPacket))
181
+ self.assertTrue(packet.dict is self.client.dict)
182
+ self.assertEqual(packet.id, 15)
183
+ self.assertEqual(packet.secret, b'zeer geheim')