From 1f6fa47eb0a14b5ef3d1013f5e0271362fc978f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 28 Sep 2021 20:15:29 +0200
Subject: [PATCH] api: fix handling of query string and errors in provision API
 (#57361)

---
 tests/test_hobo_notify.py | 52 ++++++++++++++++++++++++++++++++++++++-
 wcs/api.py                |  2 +-
 wcs/compat.py             | 14 ++++++++---
 3 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/tests/test_hobo_notify.py b/tests/test_hobo_notify.py
index 72b3e49fb..2f730f36b 100644
--- a/tests/test_hobo_notify.py
+++ b/tests/test_hobo_notify.py
@@ -1,13 +1,16 @@
+import os
 import uuid
 
 import pytest
 
+from wcs.api_utils import sign_url
 from wcs.ctl.hobo_notify import CmdHoboNotify
 from wcs.qommon import force_str
 from wcs.qommon import storage as st
+from wcs.qommon.afterjobs import AfterJob
 from wcs.qommon.http_request import HTTPRequest
 
-from .utilities import create_temporary_pub
+from .utilities import create_temporary_pub, get_app
 
 
 def pytest_generate_tests(metafunc):
@@ -24,6 +27,14 @@ def pub(request):
     pub.cfg['sp'] = {'saml2_providerid': 'test'}
     pub.write_cfg()
 
+    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
+        fd.write(
+            '''\
+[api-secrets]
+coucou = 1234
+'''
+        )
+
     pub.role_class.wipe()
     r = pub.role_class(name='Service étt civil')
     r.slug = 'service-ett-civil'
@@ -849,3 +860,42 @@ def test_process_user_deprovision_with_data(pub):
     CmdHoboNotify.process_notification(notification)
     assert User.count() == 1
     assert len(User.select([st.NotNull('deleted_timestamp')])) == 1
+
+
+def test_provision_http_endpoint(pub):
+    get_app(pub).get('/__provision__/', status=403)
+
+    get_app(pub).get(sign_url('/__provision__/?orig=coucou', '1234'), status=400)
+
+    notification = {
+        '@type': 'provision',
+        'issuer': 'http://idp.example.net/idp/saml/metadata',
+        'audience': ['test'],
+        'objects': {
+            '@type': 'user',
+            'data': [
+                {
+                    'uuid': 'a' * 32,
+                    'first_name': 'John',
+                    'last_name': 'Doé',
+                    'email': 'john.doe@example.net',
+                    'zipcode': '13400',
+                    'is_superuser': False,
+                    'is_active': True,
+                    'roles': [],
+                }
+            ],
+        },
+    }
+
+    AfterJob.wipe()
+    pub.user_class.wipe()
+    get_app(pub).post_json(sign_url('/__provision__/?orig=coucou', '1234'), notification)
+    assert AfterJob.count() == 1  # async by default
+    assert pub.user_class.count() == 1
+
+    AfterJob.wipe()
+    pub.user_class.wipe()
+    get_app(pub).post_json(sign_url('/__provision__/?orig=coucou&sync=1', '1234'), notification)
+    assert AfterJob.count() == 0  # sync
+    assert pub.user_class.count() == 1
diff --git a/wcs/api.py b/wcs/api.py
index eb842187c..ff731b69f 100644
--- a/wcs/api.py
+++ b/wcs/api.py
@@ -1345,7 +1345,7 @@ def provisionning(request):
     if not is_url_signed():
         raise AccessForbiddenError()
 
-    sync = request.form.get('sync') == '1'
+    sync = request.GET.get('sync') == '1'
 
     if sync:
         CmdHoboNotify().process_notification(get_request().json)
diff --git a/wcs/compat.py b/wcs/compat.py
index 8c0a261b5..cf2928a38 100644
--- a/wcs/compat.py
+++ b/wcs/compat.py
@@ -263,12 +263,20 @@ class PublishErrorMiddleware(MiddlewareMixin):
         if not isinstance(exception, PublishError):
             return None
         request = get_request()
-        exception_body = exception.render()
+
+        content_type = None
+        status_code = getattr(exception, 'status_code', None)
+
+        if hasattr(exception, 'render'):
+            exception_body = exception.render()
+        else:
+            exception_body = str(exception)
+            content_type = 'text/plain'
 
         django_response = HttpResponse(
             exception_body,
-            content_type=request.response.content_type,
-            status=request.response.status_code,
+            content_type=content_type or request.response.content_type,
+            status=status_code or request.response.status_code,
             reason=request.response.reason_phrase,
         )
 
-- 
2.33.0

