From 89608a2b68c668dd700fe25956989ddd2f85e1ed Mon Sep 17 00:00:00 2001
From: Bianca Severino <biancasev@gmail.com>
Date: Wed, 1 Sep 2021 14:39:42 -0400
Subject: [PATCH] fix: search by external_user_key is now case insensitive

---
 .../program_enrollments/api/reading.py        | 18 +++++++----
 lms/djangoapps/support/tests/test_views.py    | 32 +++++++++++++++++++
 .../support/views/program_enrollments.py      |  4 ++-
 3 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/lms/djangoapps/program_enrollments/api/reading.py b/lms/djangoapps/program_enrollments/api/reading.py
index 10e1c2f6035..47ee5862010 100644
--- a/lms/djangoapps/program_enrollments/api/reading.py
+++ b/lms/djangoapps/program_enrollments/api/reading.py
@@ -5,7 +5,10 @@ Outside of this subpackage, import these functions
 from `lms.djangoapps.program_enrollments.api`.
 """
 
+from functools import reduce
+from operator import or_
 
+from django.db.models import Q
 from organizations.models import Organization
 from social_django.models import UserSocialAuth
 
@@ -257,7 +260,7 @@ def fetch_program_enrollments_by_student(
         )
     filters = {
         "user": user,
-        "external_user_key": external_user_key,
+        "external_user_key__iexact": external_user_key,
         "program_uuid__in": program_uuids,
         "curriculum_uuid__in": curriculum_uuids,
         "status__in": program_enrollment_statuses,
@@ -412,11 +415,14 @@ def get_users_by_external_keys_and_org_key(external_user_keys, org_key):
             saml_provider.get_social_auth_uid(external_user_key)
             for external_user_key in external_user_keys
         }
-        social_auths = UserSocialAuth.objects.filter(uid__in=social_auth_uids)
-        found_users_by_external_keys.update({
-            saml_provider.get_remote_id_from_social_auth(social_auth): social_auth.user
-            for social_auth in social_auths
-        })
+        if social_auth_uids:
+            # Filter should be case insensitive
+            query_filter = reduce(or_, [Q(uid__iexact=uid) for uid in social_auth_uids])
+            social_auths = UserSocialAuth.objects.filter(query_filter)
+            found_users_by_external_keys.update({
+                saml_provider.get_remote_id_from_social_auth(social_auth): social_auth.user
+                for social_auth in social_auths
+            })
 
     # Default all external keys to None, because external keys
     # without a User will not appear in `found_users_by_external_keys`.
diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py
index 55de4445440..5dfe8669d42 100644
--- a/lms/djangoapps/support/tests/test_views.py
+++ b/lms/djangoapps/support/tests/test_views.py
@@ -1069,6 +1069,38 @@ class ProgramEnrollmentsInspectorViewTests(SupportViewTestCase):
         render_call_dict = mocked_render.call_args[0][1]
         assert expected_error == render_call_dict['error']
 
+    @patch_render
+    def test_search_external_user_case_insensitive(self, mocked_render):
+        external_user_key = 'AbCdEf123'
+        requested_external_user_key = 'aBcDeF123'
+
+        created_user, expected_user_info = self._construct_user(
+            'test_user_connected',
+            self.org_key_list[0],
+            external_user_key
+        )
+
+        expected_enrollments = self._construct_enrollments(
+            [self.program_uuid],
+            [self.course.id],
+            external_user_key,
+            created_user
+        )
+        id_verified = self._construct_id_verification(created_user)
+
+        self.client.get(self.url, data={
+            'external_user_key': requested_external_user_key,
+            'org_key': self.org_key_list[0]
+        })
+        expected_info = {
+            'user': expected_user_info,
+            'enrollments': expected_enrollments,
+            'id_verification': id_verified,
+        }
+
+        render_call_dict = mocked_render.call_args[0][1]
+        assert expected_info == render_call_dict['learner_program_enrollments']
+
 
 class SsoRecordsTests(SupportViewTestCase):  # lint-amnesty, pylint: disable=missing-class-docstring
 
diff --git a/lms/djangoapps/support/views/program_enrollments.py b/lms/djangoapps/support/views/program_enrollments.py
index fa42fa32bec..22435559b71 100644
--- a/lms/djangoapps/support/views/program_enrollments.py
+++ b/lms/djangoapps/support/views/program_enrollments.py
@@ -224,7 +224,9 @@ class ProgramEnrollmentsInspectorView(View):
                 [external_user_key],
                 org_key
             )
-            found_user = users_by_key.get(external_user_key)
+            # Remove entries with no corresponding user and convert keys to lowercase
+            users_by_key_lower = {key.lower(): value for key, value in users_by_key.items() if value}
+            found_user = users_by_key_lower.get(external_user_key.lower())
         except (
             BadOrganizationShortNameException,
             ProviderDoesNotExistException
-- 
GitLab