Projet

Général

Profil

0001-profile_views-add-a-profile-page-to-manage-oidc-auth.patch

Paul Marillonnet, 01 août 2020 11:30

Télécharger (12 ko)

Voir les différences:

Subject: [PATCH] profile_views: add a profile page to manage oidc-authorized
 ous (#45651)

 src/authentic2/app_settings.py                |  3 +
 .../templates/authentic2/accounts.html        |  3 +
 .../accounts_authorized_oauth_ous.html        | 67 +++++++++++++++++++
 src/authentic2/urls.py                        |  5 +-
 src/authentic2/views.py                       | 45 +++++++++++--
 tests/test_idp_oidc.py                        | 49 ++++++++++++++
 tests/test_profile.py                         | 11 +++
 7 files changed, 177 insertions(+), 6 deletions(-)
 create mode 100644 src/authentic2/templates/authentic2/accounts_authorized_oauth_ous.html
src/authentic2/app_settings.py
130 130
    A2_PROFILE_DISPLAY_SERVICE_AUTHORIZATIONS_PAGE=Setting(
131 131
        default=True,
132 132
        definition='Display page for user to revoke granted services access to its account profile data'),
133
    A2_PROFILE_DISPLAY_OU_AUTHORIZATIONS_PAGE=Setting(
134
        default=True,
135
        definition='Display page for user to revoke granted services within OUs access to its account profile data'),
133 136
    A2_PROFILE_DISPLAY_EMPTY_FIELDS=Setting(
134 137
        default=False,
135 138
        definition='Include empty fields in profile view'),
src/authentic2/templates/authentic2/accounts.html
40 40
      {% if allow_authorization_management %}
41 41
        <p><a href="{% url 'authorized-oauth-services' %}">{% trans "Manage service authorizations" %}</a></p>
42 42
      {% endif %}
43
      {% if allow_authorization_management_by_ou %}
44
        <p><a href="{% url 'authorized-oauth-ous' %}">{% trans "Manage organizational unit authorizations" %}</a></p>
45
      {% endif %}
43 46
      {% if allow_account_deletion %}
44 47
        <p><a href="{% url 'delete_account' %}">{% trans "Delete account" %}</a></p>
45 48
      {% endif %}
src/authentic2/templates/authentic2/accounts_authorized_oauth_ous.html
1
{% extends "authentic2/base-page.html" %}
2
{% load i18n %}
3

  
4
{% block page-title %}
5
{{ block.super }} - {{ view.title }}
6
{% endblock %}
7

  
8
{% block breadcrumb %}
9
{{ block.super }}
10
<a href="..">{% trans "Your account" %}</a>
11
<a href="">{{ view.title }}</a>
12
{% endblock %}
13

  
14
{% block content %}
15
{% block oidc-authorized-oauth-ous-pre %}{% endblock %}
16
<div class="authorized-oauth-ous">
17
  {% block oidc-authorized-oauth-ous-top %}
18
  <p class="authorized-oauth-ous--top">
19
    {% if authorized_oauth_ous|length_is:0 %}
20
    {% trans "You have not granted organizational unit access to your account profile data." %}
21
    {% else %}
22
    {% blocktrans count counter=authorized_oauth_ous|length %}
23
    You have granted one organizational unit access to your account profile data.
24
    {% plural %}
25
    You have granted {{ counter }} organizational units access to your account profile data.
26
    {% endblocktrans %}
27
    {% endif %}
28
  </p>
29
  {% endblock %}
30
  <ul class="authorized-oauth-ous--list">
31
    {% for auth in authorized_oauth_ous %}
32
    <li class="authorized-oauth-ous--item">
33
      <form method="post" class="authorized-oauth-ous--form">
34
        {% csrf_token %}
35
        {% block oidc-authorized-oauth-ou %}
36
        <div class="authorized-oauth-ous--infos">
37
          {% block oidc-authorized-oauth-ou-top %}{% endblock %}
38
          <span class="authorized-oauth-ous--client">
39
            {{ auth.client }}
40
          </span>
41
          <span class="authorized-oauth-ous--dates">
42
            <span class="authorized-oauth-ous-dates--since">
43
              <span class="label">{% trans "Allowed since:" %}</span>
44
              <span class="time">{{ auth.created }}</span>
45
            </span>
46
            <span class="authorized-oauth-ous--separator">/</span>
47
            <span class="authorized-oauth-ous--expired">
48
              <span class="label">{% trans "Expire on:" %}</span>
49
              <span class="time">{{ auth.expired }}</span>
50
            </span>
51
          </span>
52
        </div>
53
        <div class="authorized-oauth-ous--actions">
54
          <input type="hidden" id="auth-id" name="auth_id" value="{{ auth.id }}">
55
          <button class="authorized-oauth-ous--revoke-button">{% trans 'Revoke' %}</button>
56
        </div>
57
        {% block oidc-authorized-oauth-ou-bottom %}{% endblock %}
58
        {% endblock %}
59
      </form>
60
    </li>
61
    {% endfor %}
62
  </ul>
63
</table>
64
{% block oidc-authorized-oauth-ous-bottom %}{% endblock %}
65
</div>
66
{% block oidc-authorized-oauth-ous-post %}{% endblock %}
67
{% endblock %}
src/authentic2/urls.py
69 69
    url(r'^change-email/verify/$',
70 70
        views.email_change_verify,
71 71
        name='email-change-verify'),
72
    url(r'^authorizations/$',
72
    url(r'^service-authorizations/$',
73 73
        login_required(views.authorized_oauth_services),
74 74
        name='authorized-oauth-services'),
75
    url(r'^ou-authorizations/$',
76
        login_required(views.authorized_oauth_ous),
77
        name='authorized-oauth-ous'),
75 78
    url(r'^$',
76 79
        views.profile,
77 80
        name='account_management'),
src/authentic2/views.py
513 513
            'federation_management': federation_management,
514 514
        })
515 515

  
516
        if ('authentic2_idp_oidc' in settings.INSTALLED_APPS and
517
                app_settings.A2_PROFILE_DISPLAY_SERVICE_AUTHORIZATIONS_PAGE):
518
            from authentic2_idp_oidc.models import OIDCClient
519
            context['allow_authorization_management'] = OIDCClient.objects.filter(
520
                    authorization_mode=OIDCClient.AUTHORIZATION_MODE_BY_SERVICE).exists()
516
        if 'authentic2_idp_oidc' in settings.INSTALLED_APPS:
517
            if app_settings.A2_PROFILE_DISPLAY_SERVICE_AUTHORIZATIONS_PAGE:
518
                from authentic2_idp_oidc.models import OIDCClient
519
                context['allow_authorization_management'] = OIDCClient.objects.filter(
520
                        authorization_mode=OIDCClient.AUTHORIZATION_MODE_BY_SERVICE).exists()
521
            if app_settings.A2_PROFILE_DISPLAY_OU_AUTHORIZATIONS_PAGE:
522
                from authentic2_idp_oidc.models import OIDCClient
523
                context['allow_authorization_management_by_ou'] = OIDCClient.objects.filter(
524
                        authorization_mode=OIDCClient.AUTHORIZATION_MODE_BY_OU).exists()
521 525

  
522 526
        hooks.call_hooks('modify_context_data', self, context)
523 527
        return context
......
1309 1313

  
1310 1314

  
1311 1315
authorized_oauth_services = AuthorizedOauthServicesView.as_view()
1316

  
1317

  
1318
class AuthorizedOauthOusView(TemplateView):
1319
    template_name = 'authentic2/accounts_authorized_oauth_ous.html'
1320
    title = _('Consent Management by organizational units')
1321

  
1322
    def get_context_data(self, **kwargs):
1323
        from authentic2_idp_oidc.models import OIDCAuthorization
1324
        from django_rbac.utils import get_ou_model
1325

  
1326
        context = super(AuthorizedOauthOusView, self).get_context_data(**kwargs)
1327
        ou_ct = ContentType.objects.get_for_model(get_ou_model())
1328
        context['authorized_oauth_ous'] = OIDCAuthorization.objects.filter(
1329
            user=self.request.user, client_ct=ou_ct)
1330
        return context
1331

  
1332
    def post(self, request, *args, **kwargs):
1333
        from authentic2_idp_oidc.models import OIDCAuthorization
1334
        from django_rbac.utils import get_ou_model
1335

  
1336
        ou_ct = ContentType.objects.get_for_model(get_ou_model())
1337
        qs = OIDCAuthorization.objects.filter(
1338
            user=request.user, client_ct=ou_ct)
1339
        auth_id = request.POST.get('auth_id')
1340
        if auth_id:
1341
            qs = qs.filter(id=auth_id)
1342
        qs.delete()
1343
        return HttpResponseRedirect(reverse('authorized-oauth-ous'))
1344

  
1345

  
1346
authorized_oauth_ous = AuthorizedOauthOusView.as_view()
tests/test_idp_oidc.py
1647 1647
    assert len(response.html.find_all(
1648 1648
        'button', {'class': 'authorized-oauth-services--revoke-button'})) == 1
1649 1649
    assert "You have granted one service access to your account profile data." in response.text
1650

  
1651

  
1652
def test_oidc_authorized_oauth_ous_view(app, oidc_client, simple_user):
1653
    url = make_url('authorized-oauth-ous')
1654
    response = app.get(url, status=302)
1655
    assert '/login/' in response.location
1656

  
1657
    utils.login(app, simple_user)
1658
    response = app.get(url, status=200)
1659
    assert "You have not granted organizational unit access to your account profile data." in response.text
1660

  
1661
    OU = get_ou_model()
1662
    ou1 = OU.objects.create(name='Orgunit1', slug='orgunit1')
1663
    ou2 = OU.objects.create(name='Orgunit2', slug='orgunit2')
1664
    ou3 = OU.objects.create(name='Orgunit3', slug='orgunit3')
1665

  
1666
    oidc_client.authorization_mode = OIDCClient.AUTHORIZATION_MODE_BY_OU
1667
    oidc_client.ou = ou1
1668
    oidc_client.save()
1669

  
1670
    OIDCAuthorization.objects.create(
1671
        client=ou1, user=simple_user, scopes='openid',
1672
        expired=now() + datetime.timedelta(days=2))
1673
    OIDCAuthorization.objects.create(
1674
        client=ou2, user=simple_user, scopes='openid profile',
1675
        expired=now() + datetime.timedelta(days=2))
1676
    OIDCAuthorization.objects.create(
1677
        client=ou3, user=simple_user, scopes='openid profile email',
1678
        expired=now() + datetime.timedelta(days=2))
1679
    # create a service-based authz that should not appear here
1680
    OIDCAuthorization.objects.create(
1681
        client=oidc_client, user=simple_user, scopes='openid profile email',
1682
        expired=now() + datetime.timedelta(days=2))
1683

  
1684
    response = app.get(url, status=200)
1685
    assert "You have granted 3 organizationat units access to your account profile data."
1686
    assert len(response.html.find_all(
1687
        'button', {'class': 'authorized-oauth-ous--revoke-button'})) == 3
1688

  
1689
    # revoke two
1690
    response = response.forms[0].submit()
1691
    response = response.follow()
1692
    assert len(response.html.find_all(
1693
        'button', {'class': 'authorized-oauth-ous--revoke-button'})) == 2
1694
    response = response.forms[0].submit()
1695
    response = response.follow()
1696
    assert len(response.html.find_all(
1697
        'button', {'class': 'authorized-oauth-ous--revoke-button'})) == 1
1698
    assert "You have granted one organizational unit access to your account profile data." in response.text
tests/test_profile.py
231 231
        reverse('delete_account')
232 232
    ]
233 233

  
234
    # oidc client defined for ou-based authz management
235
    client.authorization_mode = OIDCClient.AUTHORIZATION_MODE_BY_OU
236
    client.save()
237
    response = app.get(url, status=200)
238
    assert [x['href'] for x in response.html.find('div', {'id': 'a2-profile'}).find_all('a')] == [
239
        reverse('email-change'),
240
        reverse('profile_edit'),
241
        reverse('authorized-oauth-ous'),
242
        reverse('delete_account')
243
    ]
244

  
234 245
    # restore authorization mode
235 246
    client.authorization_mode = OIDCClient.AUTHORIZATION_MODE_BY_SERVICE
236 247
    client.save()
237
-