Skip to content

Commit

Permalink
Add patch for Kolibri OSUser password prompt issue
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanmccall committed May 4, 2024
1 parent 89e0a5e commit b5320ff
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
From 6e0bcb540e56cd36917dd3edf4db218ee576e47a Mon Sep 17 00:00:00 2001
From: Richard Tibbles <richard@learningequality.org>
Date: Tue, 30 Apr 2024 16:51:48 -0700
Subject: [PATCH] Don't let users that are used for os user have their
passwords set via the not specified password flow.

---
kolibri/core/auth/api.py | 8 +-
kolibri/core/auth/test/test_api.py | 150 +++++++++++++++++++++++++++++
2 files changed, 156 insertions(+), 2 deletions(-)

diff --git a/kolibri/core/auth/api.py b/kolibri/core/auth/api.py
index 3ed56db2cd..689d399581 100644
--- a/kolibri/core/auth/api.py
+++ b/kolibri/core/auth/api.py
@@ -854,7 +854,7 @@ class SetNonSpecifiedPasswordView(views.APIView):
except (ValueError, ObjectDoesNotExist):
raise Http404(error_message)

- if user.password != NOT_SPECIFIED:
+ if user.password != NOT_SPECIFIED or hasattr(user, "os_user"):
raise Http404(error_message)

user.set_password(password)
@@ -930,10 +930,14 @@ class SessionViewSet(viewsets.ViewSet):
unauthenticated_user = FacilityUser.objects.filter(
username__exact=username, facility=facility_id
).first()
- if unauthenticated_user.password == NOT_SPECIFIED:
+ if unauthenticated_user.password == NOT_SPECIFIED and not hasattr(
+ unauthenticated_user, "os_user"
+ ):
# Here - we have a Learner whose password is "NOT_SPECIFIED" because they were created
# while the "Require learners to log in with password" setting was disabled - but now
# it is enabled again.
+ # Alternatively, they may have been created as an OSUser for automatic login with an
+ # authentication token. If this is the case, then we do not allow for the password to be set.
return Response(
[
{
diff --git a/kolibri/core/auth/test/test_api.py b/kolibri/core/auth/test/test_api.py
index 5ea87b39b3..aab92784a6 100644
--- a/kolibri/core/auth/test/test_api.py
+++ b/kolibri/core/auth/test/test_api.py
@@ -30,6 +30,7 @@ from .helpers import provision_device
from kolibri.core import error_constants
from kolibri.core.auth.backends import FACILITY_CREDENTIAL_KEY
from kolibri.core.auth.constants import demographics
+from kolibri.core.device.models import OSUser
from kolibri.core.device.utils import set_device_settings

# A weird hack because of http://bugs.python.org/issue17866
@@ -1309,6 +1310,40 @@ class LoginLogoutTestCase(APITestCase):

self.assertEqual(response_user3.status_code, 401)

+ def test_not_specified_password(self):
+ self.user.password = demographics.NOT_SPECIFIED
+ self.user.save()
+
+ response = self.client.post(
+ reverse("kolibri:core:session-list"),
+ data={
+ "username": self.user.username,
+ "facility": self.facility.id,
+ },
+ format="json",
+ )
+
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(response.data[0]["id"], error_constants.PASSWORD_NOT_SPECIFIED)
+
+ def test_not_specified_password_os_user(self):
+ self.user.password = demographics.NOT_SPECIFIED
+ self.user.save()
+
+ OSUser.objects.create(user=self.user, os_username="os_user")
+
+ response = self.client.post(
+ reverse("kolibri:core:session-list"),
+ data={
+ "username": self.user.username,
+ "facility": self.facility.id,
+ },
+ format="json",
+ )
+
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(response.data[0]["id"], error_constants.MISSING_PASSWORD)
+

class SignUpBase(object):
@classmethod
@@ -1982,3 +2017,118 @@ class DuplicateUsernameTestCase(APITestCase):
format="json",
)
self.assertEqual(response.data, True)
+
+
+class SetNonSpecifiedPasswordViewTestCase(APITestCase):
+ def setUp(self):
+ self.url = reverse("kolibri:core:setnonspecifiedpassword")
+ self.facility = FacilityFactory.create()
+ self.user = models.FacilityUser.objects.create(
+ username="testuser",
+ facility=self.facility,
+ password=demographics.NOT_SPECIFIED,
+ )
+
+ def test_set_non_specified_password(self):
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "password": "newpassword",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 200 OK status code
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ # Refresh the user object from the database
+ self.user.refresh_from_db()
+
+ # Check that the password has been updated
+ self.assertTrue(self.user.check_password("newpassword"))
+
+ def test_set_non_specified_password_invalid_facility(self):
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "password": "newpassword",
+ "facility": uuid.uuid4().hex,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 404 Not Found status code
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_set_non_specified_password_missing_facility(self):
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "password": "newpassword",
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 400 Bad Request status code
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+ def test_set_non_specified_password_invalid_username(self):
+ # Make a POST request to set the password
+ data = {
+ "username": "invalidusername",
+ "password": "newpassword",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 404 Not Found status code
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_set_non_specified_password_missing_username(self):
+ # Make a POST request to set the password
+ data = {
+ "password": "newpassword",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 400 Bad Request status code
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+ def test_set_non_specified_password_missing_password(self):
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 400 Bad Request status code
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+ def test_set_non_specified_password_password_is_specified(self):
+ self.user.set_password("password")
+ self.user.save()
+
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "password": "newpassword",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 404 Not Found status code
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_set_non_specified_password_user_is_os_user(self):
+ OSUser.objects.create(user=self.user, os_username="osuser")
+
+ # Make a POST request to set the password
+ data = {
+ "username": "testuser",
+ "password": "newpassword",
+ "facility": self.facility.id,
+ }
+ response = self.client.post(self.url, data)
+
+ # Check that the response has a 400 Bad Request status code
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
--
2.44.0

1 change: 1 addition & 0 deletions build-aux/flatpak/modules/python3-kolibri.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"buildsystem": "simple",
"build-commands": [
"pip3 install --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} kolibri",
"patch -d ${KOLIBRI_MODULE_PATH} -p2 < 0001-Don-t-let-users-that-are-used-for-os-user-have-their.patch",
"patch -d ${KOLIBRI_MODULE_PATH}/dist/ifcfg -p3 < dist_ifcfg/0001-Remove-needless-ifcfg-warning.patch"
],
"sources": [
Expand Down

0 comments on commit b5320ff

Please sign in to comment.