diff --git a/openwisp_radius/api/freeradius_views.py b/openwisp_radius/api/freeradius_views.py index b69232e5..0ff5614e 100644 --- a/openwisp_radius/api/freeradius_views.py +++ b/openwisp_radius/api/freeradius_views.py @@ -105,7 +105,9 @@ def _check_client_ip_and_return(self, request, uuid): for ip in ip_list: try: - if ipaddress.ip_address(client_ip) in ipaddress.ip_network(ip): + if ipaddress.ip_address(client_ip) in ipaddress.ip_network( + (ip or "").strip(), strict=False + ): return (AnonymousUser(), uuid) except ValueError: invalid_addr_message = _( diff --git a/openwisp_radius/base/models.py b/openwisp_radius/base/models.py index 808b8640..909426fa 100644 --- a/openwisp_radius/base/models.py +++ b/openwisp_radius/base/models.py @@ -1338,8 +1338,11 @@ def __str__(self): @property def freeradius_allowed_hosts_list(self): addresses = [] - if self.freeradius_allowed_hosts: - addresses = self.freeradius_allowed_hosts.split(",") + addresses = [ + (ip or "").strip() + for ip in (self.freeradius_allowed_hosts or "").split(",") + if (ip or "").strip() + ] return addresses @property @@ -1383,7 +1386,9 @@ def _clean_freeradius_allowed_hosts(self): else: try: for ip_address in allowed_hosts_set: - ipaddress.ip_network(ip_address) + ip_str = (ip_address or "").strip() + if ip_str: + ipaddress.ip_network(ip_str, strict=False) except ValueError: raise ValidationError( { diff --git a/openwisp_radius/tests/test_api/test_freeradius_api.py b/openwisp_radius/tests/test_api/test_freeradius_api.py index 64968edd..82d022f5 100644 --- a/openwisp_radius/tests/test_api/test_freeradius_api.py +++ b/openwisp_radius/tests/test_api/test_freeradius_api.py @@ -2385,6 +2385,67 @@ def test_ip_from_radsetting_valid(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data, _AUTH_TYPE_ACCEPT_RESPONSE) + def test_ip_from_radsetting_cidr_range_valid(self): + with mock.patch(self.freeradius_hosts_path, []): + radsetting = OrganizationRadiusSettings.objects.get( + organization=self._get_org() + ) + radsetting.freeradius_allowed_hosts = "172.18.0.0/16" + radsetting.save() + + with mock.patch( + "openwisp_radius.api.freeradius_views.get_client_ip", + return_value=("172.18.0.10", True), + ): + response = self.client.post(reverse("radius:authorize"), self.params) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, _AUTH_TYPE_ACCEPT_RESPONSE) + + def test_ip_from_radsetting_spaces_and_host_bits_valid(self): + org = self._get_org() + with mock.patch(self.freeradius_hosts_path, []): + radsetting = OrganizationRadiusSettings.objects.get(organization=org) + + radsetting.freeradius_allowed_hosts = "127.0.0.1, 172.18.0.5/16" + radsetting.save() + + self.assertEqual( + cache.get(f"ip-{org.pk}"), + ["127.0.0.1", "172.18.0.5/16"], + ) + + with mock.patch( + "openwisp_radius.api.freeradius_views.get_client_ip", + return_value=("172.18.0.10", True), + ): + response = self.client.post(reverse("radius:authorize"), self.params) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, _AUTH_TYPE_ACCEPT_RESPONSE) + + @capture_any_output() + def test_ip_outside_cidr_range_rejected(self): + with mock.patch(self.freeradius_hosts_path, []): + radsetting = OrganizationRadiusSettings.objects.get( + organization=self._get_org() + ) + radsetting.freeradius_allowed_hosts = "172.18.0.0/16" + radsetting.save() + + with mock.patch( + "openwisp_radius.api.freeradius_views.get_client_ip", + return_value=("10.0.0.5", True), + ): + response = self.client.post(reverse("radius:authorize"), self.params) + + self.assertEqual(response.status_code, 403) + self.assertEqual( + response.data["detail"], + "Request rejected: Client IP address (10.0.0.5) is not in " + "the list of IP addresses allowed to consume the freeradius API.", + ) + def test_ip_from_setting_valid(self): response = self.client.post(reverse("radius:authorize"), self.params) self.assertEqual(response.status_code, 200)