From f007949ea6842996af787326f5d0e7f5c6c67cca Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Sat, 1 Aug 2020 09:18:46 +0200
Subject: [PATCH] profile_views: include ous in oidc-authz management page
 (#45651)

---
 .../accounts_authorized_oauth_services.html   |  8 ++---
 src/authentic2/views.py                       | 27 ++++++++++-----
 tests/test_idp_oidc.py                        | 33 +++++++++++++++----
 3 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/src/authentic2/templates/authentic2/accounts_authorized_oauth_services.html b/src/authentic2/templates/authentic2/accounts_authorized_oauth_services.html
index be72fa426..38f15a536 100644
--- a/src/authentic2/templates/authentic2/accounts_authorized_oauth_services.html
+++ b/src/authentic2/templates/authentic2/accounts_authorized_oauth_services.html
@@ -17,13 +17,9 @@
   {% block oidc-authorized-oauth-services-top %}
   <p class="authorized-oauth-services--top">
     {% if authorized_oauth_services|length_is:0 %}
-    {% trans "You have not granted service access to your account profile data." %}
+    {% trans "You have not given any authorization to access your account profile data." %}
     {% else %}
-    {% blocktrans count counter=authorized_oauth_services|length %}
-    You have granted one service access to your account profile data.
-    {% plural %}
-    You have granted {{ counter }} services access to your account profile data.
-    {% endblocktrans %}
+    {% trans "You have given authorizations to access your account profile data." %}
     {% endif %}
   </p>
   {% endblock %}
diff --git a/src/authentic2/views.py b/src/authentic2/views.py
index aa6b0ad0f..d0aadab45 100644
--- a/src/authentic2/views.py
+++ b/src/authentic2/views.py
@@ -23,6 +23,7 @@ import re
 from ratelimit.utils import is_ratelimited
 
 from django.conf import settings
+from django.contrib.contenttypes.models import ContentType
 from django.shortcuts import render, get_object_or_404
 from django.template.loader import render_to_string
 from django.views.generic.edit import UpdateView, FormView
@@ -512,12 +513,13 @@ class ProfileView(cbv.TemplateNamesMixin, TemplateView):
             'federation_management': federation_management,
         })
 
-        if ('authentic2_idp_oidc' in settings.INSTALLED_APPS and
-                app_settings.A2_PROFILE_CAN_MANAGE_SERVICE_AUTHORIZATIONS):
-            from authentic2_idp_oidc.models import OIDCClient
-            context['allow_authorization_management'] = OIDCClient.objects.filter(
-                    authorization_mode=OIDCClient.AUTHORIZATION_MODE_BY_SERVICE).exists()
-
+        if 'authentic2_idp_oidc' in settings.INSTALLED_APPS:
+            if app_settings.A2_PROFILE_CAN_MANAGE_SERVICE_AUTHORIZATIONS:
+                from authentic2_idp_oidc.models import OIDCClient
+                context['allow_authorization_management'] = OIDCClient.objects.filter(
+                        authorization_mode__in=(
+                            OIDCClient.AUTHORIZATION_MODE_BY_SERVICE,
+                            OIDCClient.AUTHORIZATION_MODE_BY_OU)).exists()
         hooks.call_hooks('modify_context_data', self, context)
         return context
 
@@ -1285,16 +1287,25 @@ class AuthorizedOauthServicesView(TemplateView):
 
     def get_context_data(self, **kwargs):
         from authentic2_idp_oidc.models import OIDCAuthorization
+        from authentic2_idp_oidc.models import OIDCClient
+        from django_rbac.utils import get_ou_model
 
         context = super(AuthorizedOauthServicesView, self).get_context_data(**kwargs)
+        service_ct = ContentType.objects.get_for_model(OIDCClient)
+        ou_ct = ContentType.objects.get_for_model(get_ou_model())
         context['authorized_oauth_services'] = OIDCAuthorization.objects.filter(
-            user=self.request.user)
+            user=self.request.user, client_ct__in=(ou_ct, service_ct,))
         return context
 
     def post(self, request, *args, **kwargs):
         from authentic2_idp_oidc.models import OIDCAuthorization
+        from authentic2_idp_oidc.models import OIDCClient
+        from django_rbac.utils import get_ou_model
 
-        qs = OIDCAuthorization.objects.filter(user=request.user)
+        service_ct = ContentType.objects.get_for_model(OIDCClient)
+        ou_ct = ContentType.objects.get_for_model(get_ou_model())
+        qs = OIDCAuthorization.objects.filter(
+            user=request.user, client_ct__in=(ou_ct, service_ct,))
         auth_id = request.POST.get('auth_id')
         if auth_id:
             qs = qs.filter(id=auth_id)
diff --git a/tests/test_idp_oidc.py b/tests/test_idp_oidc.py
index 261c1b7a9..b26a83dfc 100644
--- a/tests/test_idp_oidc.py
+++ b/tests/test_idp_oidc.py
@@ -45,6 +45,7 @@ from authentic2_idp_oidc.utils import make_sub
 from authentic2.a2_rbac.utils import get_default_ou
 from authentic2.utils import make_url
 from authentic2_auth_oidc.utils import parse_timestamp
+from django_rbac.utils import get_ou_model
 from django_rbac.utils import get_role_model
 
 User = get_user_model()
@@ -1615,14 +1616,23 @@ def test_oidc_client_clean():
 
 
 def test_oidc_authorized_oauth_services_view(app, oidc_client, simple_user):
+    from django.contrib.contenttypes.models import ContentType
+
     url = make_url('authorized-oauth-services')
     response = app.get(url, status=302)
     assert '/login/' in response.location
 
     utils.login(app, simple_user)
     response = app.get(url, status=200)
-    assert "You have not granted service access to your account profile data." in response.text
+    assert "You have not given any authorization to access your account profile data." in response.text
 
+    # create an ou authz
+    OU = get_ou_model()
+    ou1 = OU.objects.create(name='Orgunit1', slug='orgunit1')
+    OIDCAuthorization.objects.create(
+        client=ou1, user=simple_user, scopes='openid profile email',
+        expired=now() + datetime.timedelta(days=2))
+    # create service authzs
     OIDCAuthorization.objects.create(
         client=oidc_client, user=simple_user, scopes='openid',
         expired=now() + datetime.timedelta(days=2))
@@ -1634,17 +1644,28 @@ def test_oidc_authorized_oauth_services_view(app, oidc_client, simple_user):
         expired=now() + datetime.timedelta(days=2))
 
     response = app.get(url, status=200)
-    assert "You have granted 3 services access to your account profile data."
+    assert "You have given authorizations to access your account profile data." in response.text
     assert len(response.html.find_all(
-        'button', {'class': 'authorized-oauth-services--revoke-button'})) == 3
+        'button', {'class': 'authorized-oauth-services--revoke-button'})) == 4
 
-    # revoke two
-    response = response.forms[0].submit()
+    # revoke two service authz
+    response = response.forms[1].submit()
+    response = response.follow()
+    assert len(response.html.find_all(
+        'button', {'class': 'authorized-oauth-services--revoke-button'})) == 3
+    assert OIDCAuthorization.objects.filter(
+        client_ct=ContentType.objects.get_for_model(OIDCClient)).count() == 2
+    response = response.forms[1].submit()
     response = response.follow()
     assert len(response.html.find_all(
         'button', {'class': 'authorized-oauth-services--revoke-button'})) == 2
+    assert OIDCAuthorization.objects.filter(
+        client_ct=ContentType.objects.get_for_model(OIDCClient)).count() == 1
+
+    # revoke the only OU authz
     response = response.forms[0].submit()
     response = response.follow()
     assert len(response.html.find_all(
         'button', {'class': 'authorized-oauth-services--revoke-button'})) == 1
-    assert "You have granted one service access to your account profile data." in response.text
+    assert OIDCAuthorization.objects.filter(
+        client_ct=ContentType.objects.get_for_model(OU)).count() == 0
-- 
2.28.0

