From f9754f13daaa4fbd5007eed787d6d62aef62e5a5 Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Fri, 22 Oct 2021 10:41:44 +0200
Subject: [PATCH] api: add a full_known_users option to /synchronization/
 endpoint (#57567)

---
 src/authentic2/api_views.py | 15 +++++++++++++--
 tests/test_api.py           | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py
index 04406d94..441da0a7 100644
--- a/src/authentic2/api_views.py
+++ b/src/authentic2/api_views.py
@@ -795,11 +795,17 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin
 
     class SynchronizationSerializer(serializers.Serializer):
         known_uuids = serializers.ListField(child=serializers.CharField())
+        full_known_users = serializers.BooleanField(required=False)
 
     def check_uuids(self, uuids):
         User = get_user_model()
         known_uuids = User.objects.filter(uuid__in=uuids).values_list('uuid', flat=True)
-        return set(uuids) - set(known_uuids)
+        return (known_uuids, set(uuids) - set(known_uuids))
+
+    def get_users_from_uuids(self, known_uuids):
+        User = get_user_model()
+        known_users = User.objects.filter(uuid__in=known_uuids)
+        return [BaseUserSerializer(user).data for user in known_users]
 
     @action(detail=False, methods=['post'], permission_classes=(DjangoPermission('custom_user.search_user'),))
     def synchronization(self, request):
@@ -808,11 +814,16 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin
             response = {'result': 0, 'errors': serializer.errors}
             return Response(response, status.HTTP_400_BAD_REQUEST)
         hooks.call_hooks('api_modify_serializer_after_validation', self, serializer)
-        unknown_uuids = self.check_uuids(serializer.validated_data.get('known_uuids', []))
+        known_uuids, unknown_uuids = self.check_uuids(serializer.validated_data.get('known_uuids', []))
+        full_known_users = serializer.validated_data.get('full_known_users', None)
         data = {
             'result': 1,
             'unknown_uuids': unknown_uuids,
         }
+        if full_known_users:
+            if len(known_uuids) > 1000:
+                known_uuids = known_uuids[:1000]
+            data['known_users'] = self.get_users_from_uuids(known_uuids)
         hooks.call_hooks('api_modify_response', self, 'synchronization', data)
         return Response(data)
 
diff --git a/tests/test_api.py b/tests/test_api.py
index a7fd4e7b..1357c8c0 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1150,6 +1150,40 @@ def test_user_synchronization(app, simple_user):
     assert set(response.json['unknown_uuids']) == set(unknown_uuids)
 
 
+def test_user_synchronization_full(app, admin):
+    headers = basic_authorization_header(admin)
+    uuids = []
+    for _ in range(100):
+        user = User.objects.create(first_name='jim', last_name='jam')
+        uuids.append(user.uuid)
+    unknown_uuids = [uuid.uuid4().hex for i in range(100)]
+    url = reverse('a2-api-users-synchronization')
+    content = {
+        'known_uuids': uuids + unknown_uuids,
+        'full_known_users': 1,
+    }
+    random.shuffle(content['known_uuids'])
+    response = app.post_json(url, params=content, headers=headers)
+    assert response.json['result'] == 1
+
+    # known users returned as part of api's full mode:
+    assert len(response.json['known_users']) == 100
+    for user_dict in response.json['known_users']:
+        assert user_dict['first_name'] == 'jim'
+        assert user_dict['last_name'] == 'jam'
+        assert {
+            'uuid',
+            'email',
+            'is_staff',
+            'is_superuser',
+            'email_verified',
+            'ou',
+            'is_active',
+            'deactivation',
+            'modified',
+        }.issubset(set(user_dict.keys()))
+
+
 def test_api_drf_authentication_class(app, admin, user_ou1, oidc_client):
     url = '/api/users/%s/' % user_ou1.uuid
     # test invalid client
-- 
2.33.1

