Skip to content

Commit f9ac085

Browse files
committed
[tests] Added tests
1 parent 95efdf7 commit f9ac085

8 files changed

Lines changed: 423 additions & 29 deletions

File tree

openwisp_radius/api/views.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def post(self, request, *args, **kwargs):
316316
self.update_user_details(user)
317317
context = {"view": self, "request": request}
318318
serializer = self.serializer_class(instance=token, context=context)
319-
response = RadiusUserSerializer(user).data
319+
response = RadiusUserSerializer(user, context=context).data
320320
response.update(serializer.data)
321321
status_code = 200 if user.is_active else 401
322322
# If identity verification is required, check if user is verified
@@ -336,24 +336,24 @@ def validate_membership(self, user):
336336
if get_organization_radius_settings(
337337
self.organization, "registration_enabled"
338338
):
339-
if self._needs_identity_verification(
340-
org=self.organization
341-
) and not self.is_identity_verified_strong(user, self.organization):
342-
raise PermissionDenied
343339
try:
344340
org_user = OrganizationUser(
345341
user=user, organization=self.organization
346342
)
347343
org_user.full_clean()
348344
org_user.save()
345+
RegisteredUser.objects.get_or_create(
346+
user=user,
347+
organization=self.organization,
348+
defaults={"method": ""},
349+
)
349350
except ValidationError as error:
350351
raise serializers.ValidationError(
351352
{"non_field_errors": error.message_dict.pop("__all__")}
352353
)
353354
else:
354355
message = _(
355-
"{organization} does not allow self registration "
356-
"of new accounts."
356+
"{organization} does not allow self registration of new accounts."
357357
).format(organization=self.organization.name)
358358
raise PermissionDenied(message)
359359

@@ -411,8 +411,8 @@ def post(self, request, *args, **kwargs):
411411
user.phone_number = (
412412
phone_token.phone_number if phone_token else user.phone_number
413413
)
414-
response = RadiusUserSerializer(user).data
415414
context = {"view": self, "request": request}
415+
response = RadiusUserSerializer(user, context=context).data
416416
token_data = rest_auth_settings.api_settings.TOKEN_SERIALIZER(
417417
token, context=context
418418
).data
@@ -621,11 +621,13 @@ class CreatePhoneTokenView(
621621
)
622622

623623
@swagger_auto_schema(
624-
operation_description=("""
624+
operation_description=(
625+
"""
625626
**Requires the user auth token (Bearer Token).**
626627
Used for SMS verification, sends a code via SMS to the
627628
phone number of the user.
628-
"""),
629+
"""
630+
),
629631
request_body=no_body,
630632
responses={201: ""},
631633
)
@@ -699,12 +701,14 @@ class GetPhoneTokenStatusView(DispatchOrgMixin, GenericAPIView):
699701
serializer_class = serializers.Serializer
700702

701703
@swagger_auto_schema(
702-
operation_description=("""
704+
operation_description=(
705+
"""
703706
**Requires the user auth token (Bearer Token).**
704707
Used for SMS verification, allows checking whether an active
705708
SMS token was already requested for the mobile phone number
706709
of the logged in account.
707-
"""),
710+
"""
711+
),
708712
responses={200: '`{"active":"true/false"}`'},
709713
)
710714
def get(self, request, *args, **kwargs):
@@ -772,6 +776,7 @@ def post(self, request, *args, **kwargs):
772776
},
773777
)
774778
reg_user.is_verified = True
779+
reg_user.method = "mobile_phone"
775780
# Update username if phone_number is used as username
776781
if user.username == user.phone_number:
777782
user.username = phone_token.phone_number
@@ -797,11 +802,13 @@ class ChangePhoneNumberView(ThrottledAPIMixin, CreatePhoneTokenView):
797802
serializer_class = ChangePhoneNumberSerializer
798803

799804
@swagger_auto_schema(
800-
operation_description=("""
805+
operation_description=(
806+
"""
801807
**Requires the user auth token (Bearer Token).**
802808
Allows users to change their phone number, will flag the
803809
user as inactive and send them a verification code via SMS.
804-
"""),
810+
"""
811+
),
805812
responses={200: ""},
806813
)
807814
def post(self, request, *args, **kwargs):

openwisp_radius/base/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,12 +1058,13 @@ def save_user(self, user):
10581058
OrganizationUser = swapper.load_model("openwisp_users", "OrganizationUser")
10591059
RegisteredUser = swapper.load_model("openwisp_radius", "RegisteredUser")
10601060
user.save()
1061+
radius_settings = self.organization.radius_settings
10611062
registered_user, created = RegisteredUser.get_or_create_for_user_and_org(
10621063
user=user,
10631064
organization=self.organization,
10641065
defaults={
10651066
"method": "manual",
1066-
"is_verified": self.organization.radius_settings.needs_identity_verification,
1067+
"is_verified": radius_settings.needs_identity_verification,
10671068
},
10681069
)
10691070
if (

openwisp_radius/tests/test_admin.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,20 @@ def test_inline_registered_user(self):
14071407
register_registration_method("github", "GitHub", strong_identity=False)
14081408
self.assertIn("github", RegisteredUser._weak_verification_methods)
14091409

1410+
def test_user_admin_shows_multiple_registered_user_records(self):
1411+
user = self._create_user(username="multiuser", email="multi@test.org")
1412+
org2 = self._create_org(name="org2", slug="org2")
1413+
RegisteredUser.objects.create(
1414+
user=user, organization=self.default_org, is_verified=True
1415+
)
1416+
RegisteredUser.objects.create(user=user, organization=org2, is_verified=False)
1417+
RegisteredUser.objects.create(user=user, organization=None, is_verified=True)
1418+
user_url = reverse(f"admin:{User._meta.app_label}_user_change", args=[user.pk])
1419+
response = self.client.get(user_url)
1420+
self.assertEqual(response.status_code, 200)
1421+
self.assertContains(response, "id_registered_users-TOTAL_FORMS")
1422+
self.assertIn('value="3"', response.rendered_content)
1423+
14101424
def test_get_is_verified_user_admin_list(self):
14111425
unknown = User.objects.first()
14121426
self.assertIsNotNone(unknown)

openwisp_radius/tests/test_api/test_api.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
RadiusBatch = load_model("RadiusBatch")
4242
RadiusUserGroup = load_model("RadiusUserGroup")
4343
RadiusGroup = load_model("RadiusGroup")
44+
RegisteredUser = load_model("RegisteredUser")
4445
OrganizationRadiusSettings = load_model("OrganizationRadiusSettings")
4546
Organization = swapper.load_model("openwisp_users", "Organization")
4647
OrganizationUser = swapper.load_model("openwisp_users", "OrganizationUser")
@@ -60,7 +61,7 @@ def _radius_batch_post_request(self, data, username="admin", password="tester"):
6061
login_payload = {"username": username, "password": password}
6162
login_url = reverse("radius:user_auth_token", args=[self.default_org.slug])
6263
login_response = self.client.post(login_url, data=login_payload)
63-
header = f'Bearer {login_response.json()["key"]}'
64+
header = f"Bearer {login_response.json()['key']}"
6465
url = reverse("radius:batch")
6566
return self.client.post(url, data, HTTP_AUTHORIZATION=header)
6667

@@ -381,6 +382,48 @@ def test_radius_user_serializer(self):
381382
},
382383
)
383384

385+
with self.subTest("org-specific takes precedence over global"):
386+
# Create user with both a global (unverified) and
387+
# org-specific (verified) record
388+
user2 = self._create_user(username="user2", email="user2@test.com")
389+
self._create_org_user(user=user2, organization=self.default_org)
390+
RegisteredUser.objects.create(
391+
user=user2, organization=None, is_verified=False
392+
)
393+
RegisteredUser.objects.create(
394+
user=user2,
395+
organization=self.default_org,
396+
is_verified=True,
397+
method="mobile_phone",
398+
)
399+
url = reverse("radius:user_auth_token", args=[self.default_org.slug])
400+
r = self.client.post(url, {"username": "user2", "password": "tester"})
401+
self.assertEqual(r.status_code, 200)
402+
self.assertEqual(r.data["is_verified"], True)
403+
self.assertEqual(r.data["method"], "mobile_phone")
404+
405+
with self.subTest("global record as fallback when no org-specific"):
406+
# Create user with only a global (verified) record
407+
user3 = self._create_user(username="user3", email="user3@test.com")
408+
self._create_org_user(user=user3, organization=self.default_org)
409+
RegisteredUser.objects.create(
410+
user=user3, organization=None, is_verified=True, method="email"
411+
)
412+
url = reverse("radius:user_auth_token", args=[self.default_org.slug])
413+
r = self.client.post(url, {"username": "user3", "password": "tester"})
414+
self.assertEqual(r.status_code, 200)
415+
self.assertEqual(r.data["is_verified"], True)
416+
self.assertEqual(r.data["method"], "email")
417+
418+
with self.subTest("returns None when no RegisteredUser records exist"):
419+
user4 = self._create_user(username="user4", email="user4@test.com")
420+
self._create_org_user(user=user4, organization=self.default_org)
421+
url = reverse("radius:user_auth_token", args=[self.default_org.slug])
422+
r = self.client.post(url, {"username": "user4", "password": "tester"})
423+
self.assertEqual(r.status_code, 200)
424+
self.assertIsNone(r.data["is_verified"])
425+
self.assertIsNone(r.data["method"])
426+
384427
# The fallback value is set on project startup, hence it also requires mocking.
385428
@mock.patch.object(
386429
OrganizationRadiusSettings._meta.get_field("first_name"),

openwisp_radius/tests/test_api/test_freeradius_api.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,88 @@ def test_authorize_unverified_user(self):
206206
self.assertEqual(response.status_code, 200)
207207
self.assertIsNone(response.data)
208208

209+
def test_authorize_verified_user(self):
210+
org_user = self._get_org_user()
211+
user = org_user.user
212+
org_settings = OrganizationRadiusSettings.objects.get(
213+
organization=self._get_org()
214+
)
215+
org_settings.needs_identity_verification = True
216+
org_settings.save()
217+
218+
with self.subTest("org-specific verified record passes authorization"):
219+
RegisteredUser.objects.create(
220+
user=user, organization=self._get_org(), is_verified=True
221+
)
222+
response = self._authorize_user(auth_header=self.auth_header)
223+
self.assertEqual(response.status_code, 200)
224+
self.assertEqual(response.data, {"control:Auth-Type": "Accept"})
225+
226+
with self.subTest("global verified record passes authorization (fallback)"):
227+
RegisteredUser.objects.filter(user=user).delete()
228+
RegisteredUser.objects.create(
229+
user=user, organization=None, is_verified=True
230+
)
231+
response = self._authorize_user(auth_header=self.auth_header)
232+
self.assertEqual(response.status_code, 200)
233+
self.assertEqual(response.data, {"control:Auth-Type": "Accept"})
234+
235+
def test_multi_org_user_different_verification_states(self):
236+
org1 = self._get_org()
237+
org_settings = OrganizationRadiusSettings.objects.get(organization=org1)
238+
org_settings.needs_identity_verification = True
239+
org_settings.save()
240+
org2 = self._create_org(name="org2", slug="org2")
241+
org2_settings = OrganizationRadiusSettings.objects.get_or_create(
242+
organization=org2
243+
)[0]
244+
org2_settings.needs_identity_verification = True
245+
org2_settings.full_clean()
246+
org2_settings.save()
247+
user = self._get_user_with_org()
248+
self._create_org_user(organization=org2, user=user)
249+
RegisteredUser.objects.create(user=user, organization=org1, is_verified=True)
250+
auth_header_org1 = f"Bearer {org1.pk} {org1.radius_settings.token}"
251+
response = self._authorize_user(
252+
username=user.username, auth_header=auth_header_org1
253+
)
254+
self.assertEqual(response.data["control:Auth-Type"], "Accept")
255+
256+
auth_header_org2 = f"Bearer {org2.pk} {org2.radius_settings.token}"
257+
response = self._authorize_user(
258+
username=user.username, auth_header=auth_header_org2
259+
)
260+
self.assertIsNone(response.data)
261+
262+
def test_global_fallback_for_orgs_without_specific_records(self):
263+
org1 = self._get_org()
264+
org2 = self._create_org(name="org2", slug="org2")
265+
org2_settings = OrganizationRadiusSettings.objects.get_or_create(
266+
organization=org2
267+
)[0]
268+
org2_settings.needs_identity_verification = True
269+
org2_settings.full_clean()
270+
org2_settings.save()
271+
user = self._get_user_with_org()
272+
self._create_org_user(organization=org2, user=user)
273+
RegisteredUser.objects.create(user=user, organization=None, is_verified=True)
274+
org_settings = OrganizationRadiusSettings.objects.get(organization=org1)
275+
org_settings.needs_identity_verification = True
276+
org_settings.save()
277+
user.registered_users.exclude(organization=None).delete()
278+
279+
auth_header_org1 = f"Bearer {org1.pk} {org1.radius_settings.token}"
280+
response = self._authorize_user(
281+
username=user.username, auth_header=auth_header_org1
282+
)
283+
self.assertEqual(response.data["control:Auth-Type"], "Accept")
284+
285+
auth_header_org2 = f"Bearer {org2.pk} {org2.radius_settings.token}"
286+
response = self._authorize_user(
287+
username=user.username, auth_header=auth_header_org2
288+
)
289+
self.assertEqual(response.data["control:Auth-Type"], "Accept")
290+
209291
def test_authorize_radius_token_unverified_user(self):
210292
user = self._get_org_user()
211293
org_settings = OrganizationRadiusSettings.objects.get(
@@ -258,7 +340,7 @@ def test_postauth_radius_token_accept_201(self):
258340
def test_postauth_accept_201_querystring(self):
259341
self.assertEqual(RadiusPostAuth.objects.all().count(), 0)
260342
params = self._get_postauth_params()
261-
post_url = f'{reverse("radius:postauth")}{self.token_querystring}'
343+
post_url = f"{reverse('radius:postauth')}{self.token_querystring}"
262344
response = self.client.post(post_url, params)
263345
params["password"] = ""
264346
self.assertEqual(RadiusPostAuth.objects.filter(**params).count(), 1)
@@ -2442,7 +2524,7 @@ def test_cache(self):
24422524
)
24432525
self._get_org_user()
24442526
token_querystring = f"?token={rad.token}&uuid={str(self.org.pk)}"
2445-
post_url = f'{reverse("radius:authorize")}{token_querystring}'
2527+
post_url = f"{reverse('radius:authorize')}{token_querystring}"
24462528
# Clear cache before sending request
24472529
cache.clear()
24482530
self.client.post(post_url, {"username": "tester", "password": "tester"})

0 commit comments

Comments
 (0)