Projet

Général

Profil

0001-authenticators-add-advanced-setup-view-67875.patch

Valentin Deniaud, 17 août 2022 16:17

Télécharger (10,6 ko)

Voir les différences:

Subject: [PATCH] authenticators: add advanced setup view (#67875)

 .../apps/authenticators/manager_urls.py       |  5 ++++
 .../authenticator_advanced_edit_form.html     | 20 ++++++++++++++++
 .../authenticator_edit_form.html              | 13 ++++++++++
 src/authentic2/apps/authenticators/views.py   | 18 +++++++++++++-
 src/authentic2_auth_saml/forms.py             | 24 ++++++++++++++++++-
 .../migrations/0001_initial.py                |  4 ++--
 src/authentic2_auth_saml/models.py            | 11 ++++++---
 tests/test_manager_authenticators.py          | 18 +++++++++++---
 8 files changed, 103 insertions(+), 10 deletions(-)
 create mode 100644 src/authentic2/apps/authenticators/templates/authentic2/authenticators/authenticator_advanced_edit_form.html
src/authentic2/apps/authenticators/manager_urls.py
61 61
            views.edit,
62 62
            name='a2-manager-authenticator-edit',
63 63
        ),
64
        path(
65
            'authenticators/<int:pk>/edit/advanced/',
66
            views.edit_advanced,
67
            name='a2-manager-authenticator-edit-advanced',
68
        ),
64 69
        path(
65 70
            'authenticators/<int:pk>/delete/',
66 71
            views.delete,
src/authentic2/apps/authenticators/templates/authentic2/authenticators/authenticator_advanced_edit_form.html
1
{% extends "authentic2/authenticators/authenticator_common.html" %}
2
{% load i18n gadjo %}
3

  
4
{% block breadcrumb %}
5
  {{ block.super }}
6
  <a href="{% url 'a2-manager-authenticator-detail' pk=object.pk %}">{{ object }}</a>
7
  <a href="{% url 'a2-manager-authenticator-edit' pk=object.pk %}">{% trans "Edit" %}</a>
8
  <a href="#"></a>
9
{% endblock %}
10

  
11
{% block content %}
12
  <form method="post" enctype="multipart/form-data">
13
    {% csrf_token %}
14
    {{ form|with_template }}
15
    <div class="buttons">
16
      <button>{% trans "Save" %}</button>
17
      <a class="cancel" href="{% url 'a2-manager-authenticator-detail' pk=object.pk %}">{% trans 'Cancel' %}</a>
18
    </div>
19
  </form>
20
{% endblock %}
src/authentic2/apps/authenticators/templates/authentic2/authenticators/authenticator_edit_form.html
7 7
  <a href="#"></a>
8 8
{% endblock %}
9 9

  
10
{% block appbar %}
11
  {{ block.super }}
12

  
13
  {% if object.manager_advanced_form_class %}
14
    <span class="actions">
15
      <a class="extra-actions-menu-opener"></a>
16
      <ul class="extra-actions-menu">
17
        <li><a href="{% url 'a2-manager-authenticator-edit-advanced' pk=object.pk %}">{% trans "Advanced setup" %}</a></li>
18
      </ul>
19
    </span>
20
  {% endif %}
21
{% endblock %}
22

  
10 23
{% block content %}
11 24
  <form method="post" enctype="multipart/form-data">
12 25
    {% csrf_token %}
src/authentic2/apps/authenticators/views.py
16 16

  
17 17
from django.contrib import messages
18 18
from django.core.exceptions import PermissionDenied
19
from django.http import HttpResponseRedirect
19
from django.http import Http404, HttpResponseRedirect
20 20
from django.shortcuts import get_object_or_404
21 21
from django.urls import reverse, reverse_lazy
22 22
from django.utils.functional import cached_property
......
90 90
edit = AuthenticatorEditView.as_view()
91 91

  
92 92

  
93
class AuthenticatorAdvancedEditView(AuthenticatorEditView):
94
    template_name = 'authentic2/authenticators/authenticator_advanced_edit_form.html'
95
    title = _('Edit authenticator (advanced)')
96

  
97
    def dispatch(self, *args, **kwargs):
98
        if not hasattr(self.get_object(), 'manager_advanced_form_class'):
99
            raise Http404()
100
        return super().dispatch(*args, **kwargs)
101

  
102
    def get_form_class(self):
103
        return self.object.manager_advanced_form_class
104

  
105

  
106
edit_advanced = AuthenticatorAdvancedEditView.as_view()
107

  
108

  
93 109
class AuthenticatorDeleteView(AuthenticatorsMixin, DeleteView):
94 110
    template_name = 'authentic2/authenticators/authenticator_delete_form.html'
95 111
    title = _('Delete authenticator')
src/authentic2_auth_saml/forms.py
25 25
class SAMLAuthenticatorForm(forms.ModelForm):
26 26
    class Meta:
27 27
        model = SAMLAuthenticator
28
        exclude = ('ou',)
28
        exclude = (
29
            'ou',
30
            'verify_ssl_certificate',
31
            'transient_federation_attribute',
32
            'realm',
33
            'username_template',
34
            'name_id_policy_format',
35
            'name_id_policy_allow_create',
36
            'force_authn',
37
            'add_authnrequest_next_url_extension',
38
            'group_attribute',
39
            'create_group',
40
            'error_url',
41
            'error_redirect_after_timeout',
42
            'authn_classref',
43
            'attribute_mapping',
44
        )
45

  
46

  
47
class SAMLAuthenticatorAdvancedForm(forms.ModelForm):
48
    class Meta:
49
        model = SAMLAuthenticator
50
        fields = SAMLAuthenticatorForm.Meta.exclude
29 51

  
30 52

  
31 53
class RoleChoiceField(forms.ModelChoiceField):
src/authentic2_auth_saml/migrations/0001_initial.py
81 81
                    'realm',
82 82
                    models.CharField(
83 83
                        default='saml',
84
                        help_text='The default realm to associate to user, used when setting username.',
84
                        help_text='The default realm to associate to user, can be used in username template.',
85 85
                        max_length=32,
86
                        verbose_name='Realm',
86
                        verbose_name='Realm (realm)',
87 87
                    ),
88 88
                ),
89 89
                (
src/authentic2_auth_saml/models.py
57 57
        blank=True,
58 58
    )
59 59
    realm = models.CharField(
60
        _('Realm'),
60
        _('Realm (realm)'),
61 61
        max_length=32,
62
        help_text=_('The default realm to associate to user, used when setting username.'),
62
        help_text=_('The default realm to associate to user, can be used in username template.'),
63 63
        default='saml',
64 64
    )
65 65
    username_template = models.CharField(
......
153 153
        'metadata_path',
154 154
        'metadata',
155 155
        'provision',
156
        'username_template',
157 156
    ]
158 157

  
159 158
    class Meta:
......
181 180

  
182 181
        return SAMLAuthenticatorForm
183 182

  
183
    @property
184
    def manager_advanced_form_class(self):
185
        from .forms import SAMLAuthenticatorAdvancedForm
186

  
187
        return SAMLAuthenticatorAdvancedForm
188

  
184 189
    def clean(self):
185 190
        if not (self.metadata or self.metadata_path or self.metadata_url):
186 191
            raise ValidationError(_('One of the metadata fields must be filled.'))
tests/test_manager_authenticators.py
99 99
    resp = app.get('/manage/authenticators/add/')
100 100
    assert 'Password' not in resp.text
101 101

  
102
    # password authenticator has no advanced setup form
103
    app.get('/manage/authenticators/%s/edit/advanced/' % authenticator.pk, status=404)
104
    app.get('/manage/authenticators/%s/edit/' % authenticator.pk)
105
    assert 'Advanced setup' not in resp.text
106

  
102 107

  
103 108
@pytest.mark.freeze_time('2022-04-19 14:00')
104 109
def test_authenticators_oidc(app, superuser, ou1, ou2):
......
254 259

  
255 260
    authenticator = SAMLAuthenticator.objects.filter(slug='test').get()
256 261
    resp = app.get(authenticator.get_absolute_url())
257
    assert 'Username template: {attributes[name_id_content]}@{realm}' in resp.text
258 262
    assert 'Provision: True' in resp.text
259 263
    assert 'Metadata file path' not in resp.text
260 264

  
......
262 266
    assert 'configuration is not complete' in resp.text
263 267

  
264 268
    resp = resp.click('Edit')
269
    assert 'attribute_mapping' not in resp.form.fields
270
    assert 'realm' not in resp.form.fields
271

  
265 272
    resp = resp.form.submit()
266 273
    assert 'One of the metadata fields must be filled.' in resp.text
267 274

  
268 275
    resp.form['metadata_path'] = '/var/lib/authentic2/metadata.xml'
269
    resp.form['attribute_mapping'] = '[{"attribute": "email", "saml_attribute": "mail", "mandatory": false}]'
270 276
    resp = resp.form.submit().follow()
271

  
272 277
    assert 'Metadata file path: /var/lib/authentic2/metadata.xml' in resp.text
273 278

  
279
    resp = resp.click('Edit')
280
    resp = resp.click('Advanced setup')
281
    resp.form['attribute_mapping'] = '[{"attribute": "email", "saml_attribute": "mail", "mandatory": false}]'
282
    resp.form['realm'] = 'abc'
283
    resp = resp.form.submit().follow()
284

  
274 285
    authenticator.refresh_from_db()
275 286
    assert authenticator.attribute_mapping == [
276 287
        {"attribute": "email", "saml_attribute": "mail", "mandatory": False}
277 288
    ]
289
    assert authenticator.realm == 'abc'
278 290

  
279 291
    resp = resp.click('Enable').follow()
280 292
    assert 'Authenticator has been enabled.' in resp.text
281
-