From acf058ff9fa81725b4bcc781a414875b483324e0 Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Thu, 1 Dec 2022 10:26:43 +0100
Subject: [PATCH] idp_oidc: display BO custom client config to superusers only
 (#71905)

---
 src/authentic2/manager/forms.py          |  8 +++++
 src/authentic2/manager/service_views.py  |  5 ++++
 src/authentic2_idp_oidc/manager/forms.py | 31 ++++++++++++++++++++
 src/authentic2_idp_oidc/manager/views.py |  5 ++++
 tests/idp_oidc/test_manager.py           | 37 +++++++++++++++++++++++-
 5 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/src/authentic2/manager/forms.py b/src/authentic2/manager/forms.py
index eadba6800..cc32803a7 100644
--- a/src/authentic2/manager/forms.py
+++ b/src/authentic2/manager/forms.py
@@ -940,6 +940,14 @@ class APIClientForm(forms.ModelForm):
 
 
 class ServiceForm(forms.ModelForm):
+    def __init__(self, *args, **kwargs):
+        if 'user' in kwargs:
+            # OIDC services form initialization requires knowing user permissions.
+            # this information isn't used for plain services yet.
+            # TODO stop using a generic ServiceEditView for OIDC services(?)
+            kwargs.pop('user')
+        super().__init__(*args, **kwargs)
+
     class Meta:
         model = Service
         fields = ['name', 'slug', 'ou', 'unauthorized_url']
diff --git a/src/authentic2/manager/service_views.py b/src/authentic2/manager/service_views.py
index 40a534afc..919a52123 100644
--- a/src/authentic2/manager/service_views.py
+++ b/src/authentic2/manager/service_views.py
@@ -133,6 +133,11 @@ class ServiceEditView(ServiceMixin, views.BaseEditView):
             return self.object.manager_form_class
         return super().get_form_class()
 
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['user'] = self.request.user
+        return kwargs
+
 
 edit_service = ServiceEditView.as_view()
 
diff --git a/src/authentic2_idp_oidc/manager/forms.py b/src/authentic2_idp_oidc/manager/forms.py
index 5d457acc1..06e120e92 100644
--- a/src/authentic2_idp_oidc/manager/forms.py
+++ b/src/authentic2_idp_oidc/manager/forms.py
@@ -15,6 +15,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from django import forms
+from django.contrib.auth import get_user_model
+from django.utils.translation import gettext_lazy as _
 
 from authentic2.attributes_ng.engine import get_service_attributes
 from authentic2.forms.mixins import SlugMixin
@@ -43,8 +45,37 @@ class OIDCClientForm(SlugMixin, forms.ModelForm):
         ]
 
     def __init__(self, *args, **kwargs):
+        user = kwargs.pop('user')
         super().__init__(*args, **kwargs)
         self.fields['colour'].widget = forms.TextInput(attrs={'type': 'color'})
+        if user and isinstance(user, get_user_model()) and user.is_superuser:
+            initial_has_api_access = self.instance.has_api_access if self.instance else False
+            initial_activate_user_profiles = self.instance.activate_user_profiles if self.instance else False
+            self.fields['has_api_access'] = forms.BooleanField(
+                initial=initial_has_api_access,
+                label=_("Has access to Authentic's synchronization API"),
+                required=False,
+            )
+
+            self.fields['activate_user_profiles'] = forms.BooleanField(
+                initial=initial_activate_user_profiles,
+                label=_("Activates user profiles selection"),
+                required=False,
+            )
+
+    def save(self, *args, **kwargs):
+        instance = super().save(*args, **kwargs)
+        changed = False
+        for custom_field in ('has_api_access', 'activate_user_profiles'):
+            if (
+                custom_field in self.cleaned_data
+                and getattr(instance, custom_field) != self.cleaned_data[custom_field]
+            ):
+                setattr(instance, custom_field, self.cleaned_data[custom_field])
+                changed = True
+        if changed:
+            instance.save()
+        return instance
 
 
 class OIDCClaimForm(forms.ModelForm):
diff --git a/src/authentic2_idp_oidc/manager/views.py b/src/authentic2_idp_oidc/manager/views.py
index 6cd53ce40..1cbd88cec 100644
--- a/src/authentic2_idp_oidc/manager/views.py
+++ b/src/authentic2_idp_oidc/manager/views.py
@@ -37,6 +37,11 @@ class OIDCServiceAddView(views.ActionMixin, views.BaseAddView):
             OIDCClaim.objects.get_or_create(client=self.object, **mapping)
         return reverse('a2-manager-service', kwargs={'service_pk': self.object.pk})
 
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['user'] = self.request.user
+        return kwargs
+
 
 add_oidc_service = OIDCServiceAddView.as_view()
 
diff --git a/tests/idp_oidc/test_manager.py b/tests/idp_oidc/test_manager.py
index 44d6a9dbf..c8ef957fa 100644
--- a/tests/idp_oidc/test_manager.py
+++ b/tests/idp_oidc/test_manager.py
@@ -27,7 +27,38 @@ def app(app, admin):
     return app
 
 
-def test_add_oidc_service(app):
+@pytest.fixture
+def superuser_app(app, superuser):
+    login(app, superuser)
+    return app
+
+
+def test_add_oidc_service_superuser(superuser_app):
+    resp = superuser_app.get('/manage/services/')
+    assert 'Add OIDC service' in resp.text
+    assert OIDCClient.objects.count() == 0
+    assert OIDCClaim.objects.count() == 0
+
+    resp = resp.click('Add OIDC service')
+    form = resp.form
+    form['name'] = 'Test'
+    form['redirect_uris'] = 'http://example.com'
+    form['has_api_access'] = True
+    form['activate_user_profiles'] = True
+    resp = form.submit()
+
+    assert OIDCClient.objects.count() == 1
+    assert OIDCClaim.objects.count() == len(oidc_app_settings.DEFAULT_MAPPINGS)
+    oidc_client = OIDCClient.objects.get()
+    assert oidc_client.has_api_access is True
+    assert oidc_client.activate_user_profiles is True
+    assert resp.location == f'/manage/services/{oidc_client.pk}/'
+    resp = resp.follow()
+    assert "Settings" in resp.text
+    assert "Delete" in resp.text
+
+
+def test_add_oidc_service_admin(app):
     resp = app.get('/manage/services/')
     assert 'Add OIDC service' in resp.text
     assert OIDCClient.objects.count() == 0
@@ -37,11 +68,15 @@ def test_add_oidc_service(app):
     form = resp.form
     form['name'] = 'Test'
     form['redirect_uris'] = 'http://example.com'
+    assert 'has_api_access' not in form.fields
+    assert 'activate_user_profiles' not in form.fields
     resp = form.submit()
 
     assert OIDCClient.objects.count() == 1
     assert OIDCClaim.objects.count() == len(oidc_app_settings.DEFAULT_MAPPINGS)
     oidc_client = OIDCClient.objects.get()
+    assert oidc_client.has_api_access is False
+    assert oidc_client.activate_user_profiles is False
     assert resp.location == f'/manage/services/{oidc_client.pk}/'
     resp = resp.follow()
     assert "Settings" in resp.text
-- 
2.38.1

