Skip to content

Commit 1a535cf

Browse files
committed
[chores] Handled organization agnostic Accounting-On requests #617
Accounting-On/Off requests may not be tied to a specific organization. Before we were just ignoring these requests. Now we check the IP is coming from one of authorized IP addresses, Accounting-Off requests are still ignored but Accounting-On requests will close stale sessions, regardless of whether the request is tied to a specific organization or not. Related to #617
1 parent 9f8443a commit 1a535cf

3 files changed

Lines changed: 40 additions & 22 deletions

File tree

openwisp_radius/api/freeradius_views.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
_TOKEN_AUTH_FAILED = _("Token authentication failed")
4747
# Accounting-Off is not implemented and hence ignored right now
4848
# may be implemented in the future
49-
UNSUPPORTED_STATUS_TYPES = ["Accounting-Off"]
49+
# Accounting-On is used to close stale sessions
50+
SPECIAL_STATUS_TYPES = ["Accounting-On", "Accounting-Off"]
5051
logger = logging.getLogger(__name__)
5152

5253
RadiusToken = load_model("RadiusToken")
@@ -78,16 +79,17 @@ class Meta:
7879

7980

8081
class FreeradiusApiAuthentication(BaseAuthentication):
81-
def _get_ip_list(self, uuid):
82-
if f"ip-{uuid}" in cache:
82+
def _get_ip_list(self, uuid=None):
83+
ip_list = None
84+
if uuid and f"ip-{uuid}" in cache:
8385
ip_list = cache.get(f"ip-{uuid}")
84-
else:
86+
elif uuid:
8587
try:
8688
ip_list = OrganizationRadiusSettings.objects.get(
8789
organization__pk=uuid
8890
).freeradius_allowed_hosts_list
8991
except OrganizationRadiusSettings.DoesNotExist:
90-
ip_list = None
92+
pass
9193
else:
9294
cache.set(f"ip-{uuid}", ip_list)
9395
return ip_list or app_settings.FREERADIUS_ALLOWED_HOSTS
@@ -168,8 +170,8 @@ def authenticate(self, request):
168170
self.check_organization(request)
169171
uuid, token = self.get_uuid_token(request)
170172
if not uuid and not token:
171-
if request.data.get("status_type", None) in UNSUPPORTED_STATUS_TYPES:
172-
return
173+
if request.data.get("status_type", None) in SPECIAL_STATUS_TYPES:
174+
return self._check_client_ip_and_return(request, uuid)
173175
username = request.data.get("username") or request.query_params.get(
174176
"username"
175177
)
@@ -499,15 +501,15 @@ def post(self, request, *args, **kwargs):
499501
does not return any JSON response so that freeradius will avoid
500502
processing the response without generating warnings
501503
"""
502-
if request.user.is_anonymous and request.auth is None:
503-
return Response(status=status.HTTP_200_OK)
504504
data = request.data.copy()
505505
status_type = data.get("status_type", None)
506-
if status_type in UNSUPPORTED_STATUS_TYPES:
507-
return Response(None)
508-
if status_type == "Accounting-On":
509-
self._handle_accounting_on(data)
510-
return Response(None)
506+
# Special Cases
507+
if (
508+
request.user.is_anonymous and request.auth is None
509+
) or status_type in SPECIAL_STATUS_TYPES:
510+
if status_type == "Accounting-On":
511+
self._handle_accounting_on(data)
512+
return Response(status=status.HTTP_200_OK)
511513
# Create or Update
512514
try:
513515
instance = self.get_queryset().get(unique_id=data.get("unique_id"))
@@ -547,8 +549,7 @@ def _handle_accounting_on(self, data):
547549
"""
548550
called_station_id = data.get("called_station_id")
549551
closed_count = RadiusAccounting._close_stale_sessions_on_nas_boot(
550-
called_station_id=called_station_id,
551-
organization_id=self.request.auth,
552+
called_station_id=called_station_id
552553
)
553554
if closed_count:
554555
logger.info(

openwisp_radius/base/models.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,15 +558,14 @@ def close_stale_sessions(cls, days=None, hours=None):
558558
session.save()
559559

560560
@classmethod
561-
def _close_stale_sessions_on_nas_boot(cls, called_station_id, organization_id):
561+
def _close_stale_sessions_on_nas_boot(cls, called_station_id):
562562
"""
563563
Called during RADIUS Accounting-On.
564564
"""
565-
if not called_station_id or not organization_id:
565+
if not called_station_id:
566566
return 0
567567
stale_sessions = cls.objects.filter(
568568
called_station_id=called_station_id,
569-
organization_id=organization_id,
570569
stop_time__isnull=True,
571570
)
572571
closed_count = stale_sessions.update(

openwisp_radius/tests/test_api/test_freeradius_api.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,6 @@ def test_accounting_when_nas_using_pfsense_started(self):
12211221
response = self.client.post(
12221222
self._acct_url,
12231223
data=json.dumps(data),
1224-
HTTP_AUTHORIZATION=self.auth_header,
12251224
content_type="application/json",
12261225
)
12271226
self.assertEqual(response.status_code, 200)
@@ -1311,10 +1310,29 @@ def test_accounting_on_closes_stale_sessions(self):
13111310
# Simulate Accounting-On packet from the first NAS
13121311
accounting_on_data = {
13131312
"status_type": "Accounting-On",
1313+
"session_id": "c2bc87808f568e3c",
1314+
"unique_id": "2d124bdc1d269430629970d5f0bb7113",
1315+
"username": "",
1316+
"realm": "",
1317+
"nas_ip_address": "192.168.0.4",
1318+
"nas_port_id": "0",
1319+
"nas_port_type": "",
1320+
"session_time": "",
1321+
"authentication": "",
1322+
"input_octets": "",
1323+
"output_octets": "",
13141324
"called_station_id": "AA-BB-CC-DD-EE-FF",
1315-
"unique_id": "accounting-on-packet-id",
1325+
"calling_station_id": "",
1326+
"terminate_cause": "",
1327+
"service_type": "",
1328+
"framed_protocol": "",
1329+
"framed_ip_address": "",
13161330
}
1317-
response = self.post_json(accounting_on_data)
1331+
response = self.client.post(
1332+
self._acct_url,
1333+
data=json.dumps(accounting_on_data),
1334+
content_type="application/json",
1335+
)
13181336
self.assertEqual(response.status_code, 200)
13191337
self.assertIsNone(response.data)
13201338
self.assertEqual(RadiusAccounting.objects.count(), 2)

0 commit comments

Comments
 (0)