Projet

Général

Profil

0001-manager-add-a-technical-info-page-with-ldap-configs-.patch

Paul Marillonnet, 01 décembre 2021 16:26

Télécharger (10,4 ko)

Voir les différences:

Subject: [PATCH] manager: add a technical info page with ldap configs (#58938)

 .../static/authentic2/manager/css/style.css   |   9 ++
 .../authentic2/manager/homepage.html          |   3 +
 .../authentic2/manager/ldap_details.html      |  14 +++
 .../authentic2/manager/tech_info.html         |  17 +++
 src/authentic2/manager/urls.py                |   2 +
 src/authentic2/manager/views.py               |  37 ++++++
 tests/test_manager.py                         | 109 +++++++++++++++++-
 7 files changed, 189 insertions(+), 2 deletions(-)
 create mode 100644 src/authentic2/manager/templates/authentic2/manager/ldap_details.html
 create mode 100644 src/authentic2/manager/templates/authentic2/manager/tech_info.html
src/authentic2/manager/static/authentic2/manager/css/style.css
277 277
a.role-inheritance-view-all {
278 278
    font-style: italic;
279 279
}
280

  
281
div.a2-manager-ldap pre {
282
	background: white;
283
	border: 1px solid black;
284
	padding: .3em;
285
	overflow-x: auto;
286
	white-space: pre-wrap;
287
	word-wrap: break-word;
288
}
src/authentic2/manager/templates/authentic2/manager/homepage.html
17 17
    {% if user.is_superuser or can_view_journal %}
18 18
    <li><a href="{% url 'a2-manager-journal' %}">{% trans 'Journal' %}</a></li>
19 19
    {% endif %}
20
    {% if user.is_superuser %}
21
    <li><a href="{% url 'a2-manager-tech-info' %}">{% trans 'Technical information' %}</a></li>
22
    {% endif %}
20 23
  </ul>
21 24
  </span>
22 25
  {% endif %}
src/authentic2/manager/templates/authentic2/manager/ldap_details.html
1
{% load i18n %}
2
<h4>{% trans "Realm:" %} {{ ldap.realm }}</h4>
3
<div class="a2-manager-ldap-{{ ldap.realm }}">
4
{% if not error %}
5
<h5>{% blocktrans %}Base ldapsearch command:{% endblocktrans %}</h5>
6
 <pre>ldapsearch -v -H {{ ldap.ldap_uri }} -D "{{ ldap.binddn }}" -w "{{ ldap.bindpw }}" -b "{{ ldap.basedn }}"{% if ldap.user_filter or ldap.sync_ldap_users_filter %} "{% firstof ldap.sync_ldap_users_filter ldap.user_filter %}"{% endif %}</pre>
7
{% else %}
8
<div class="error">
9
{% blocktrans %}Error while attempting to connect to LDAP server, base ldapsearch command won't be displayed.{% endblocktrans %}
10
</div>
11
{% endif %}
12
<h5>{% trans "Configuration:" %}</h5>
13
<pre>{{ ldap|pprint }}</pre>
14
</div>
src/authentic2/manager/templates/authentic2/manager/tech_info.html
1
{% extends "authentic2/manager/base.html" %}
2
{% load i18n %}
3

  
4
{% block appbar %}
5
  <h2>{% blocktrans %}{{ title }}{% endblocktrans %}</h2>
6
{% endblock %}
7

  
8
{% block content %}
9
  {% if ldap_list %}
10
  <h3>{% trans "LDAP information" %}</h3>
11
  <div id="a2-manager-tech-info-ldap-list">
12
    {% for ldap in ldap_list %}
13
     {% include "authentic2/manager/ldap_details.html" with ldap=ldap %}
14
    {% endfor %}
15
  </div>
16
{% endif %}
17
{% endblock %}
src/authentic2/manager/urls.py
181 181
        # general management
182 182
        url(r'^site-export/$', views.site_export, name='a2-manager-site-export'),
183 183
        url(r'^site-import/$', views.site_import, name='a2-manager-site-import'),
184
        # technical information including ldap config
185
        url(r'^tech-info/$', views.tech_info, name='a2-manager-tech-info'),
184 186
    ],
185 187
)
186 188

  
src/authentic2/manager/views.py
661 661
homepage = HomepageView.as_view()
662 662

  
663 663

  
664
class TechnicalInformationView(TitleMixin, MediaMixin, TemplateView):
665
    template_name = 'authentic2/manager/tech_info.html'
666
    title = _("Technical information")
667

  
668
    def get(self, request, *args, **kwargs):
669
        if not request.user.is_superuser:
670
            raise PermissionDenied
671
        return super().get(request, *args, **kwargs)
672

  
673
    def get_context_data(self, **kwargs):
674
        import ldap
675

  
676
        from authentic2.backends import ldap_backend
677

  
678
        backend = ldap_backend.LDAPBackend
679
        kwargs['ldap_list'] = []
680
        for block in backend.get_config():
681
            config = block.copy()
682
            conn = backend.get_connection(config)
683
            if not conn:
684
                kwargs['error'] = True
685
            else:
686
                # retrieve ldap uri, not directly visible in configuration block
687
                config['ldap_uri'] = conn.get_option(ldap.OPT_URI)
688
                # user filters need to be formatted to ldapsearch syntax
689
                config['user_filter'] = force_text(block.get('user_filter'), '').replace('%s', '*')
690
                config['sync_ldap_users_filter'] = (
691
                    force_text(block.get('sync_ldap_users_filter'), '').replace('%s', '*').replace('%s', '*')
692
                )
693

  
694
            kwargs['ldap_list'].append(config)
695
        return super().get_context_data(**kwargs)
696

  
697

  
698
tech_info = TechnicalInformationView.as_view()
699

  
700

  
664 701
class MenuJson(HomepageView):
665 702
    def get(self, request, *args, **kwargs):
666 703
        menu_entries = []
tests/test_manager.py
24 24
from django.core import mail
25 25
from django.test.utils import override_settings
26 26
from django.urls import reverse
27
from django.utils.encoding import force_bytes, force_str
27
from django.utils.encoding import force_bytes, force_str, force_text
28 28
from webtest import Upload
29 29

  
30 30
from authentic2.a2_rbac.models import MANAGE_MEMBERS_OP
......
37 37
from django_rbac.models import VIEW_OP
38 38
from django_rbac.utils import get_operation
39 39

  
40
from .utils import assert_event, get_link_from_mail, login, request_select2
40
from .test_ldap import slapd
41
from .utils import assert_event, get_link_from_mail, login, logout, request_select2
41 42

  
42 43
pytestmark = pytest.mark.django_db
43 44

  
......
1197 1198
    resp = resp.form.submit()
1198 1199
    assert 'Test Service' in resp.text
1199 1200
    assert 'Example Service' not in resp.text
1201

  
1202

  
1203
def test_technical_info_ldap(app, admin, superuser, slapd, settings, monkeypatch):
1204
    from ldap.dn import escape_dn_chars
1205

  
1206
    from authentic2.backends import ldap_backend
1207

  
1208
    settings.LDAP_AUTH_SETTINGS = [
1209
        {
1210
            'url': [slapd.ldap_url],
1211
            'binddn': force_text('cn=%s,o=ôrga' % escape_dn_chars('Étienne Michu')),
1212
            'bindpw': 'passé',
1213
            'basedn': 'o=ôrga',
1214
            'use_tls': False,
1215
        }
1216
    ]
1217
    assert slapd
1218

  
1219
    login(app, admin, 'a2-manager-homepage')
1220
    app.get(reverse('a2-manager-tech-info'), status=403)
1221
    logout(app)
1222

  
1223
    resp = login(app, superuser, 'a2-manager-tech-info')
1224
    ldap_config_text = resp.pyquery('div#a2-manager-tech-info-ldap-list').text()
1225

  
1226
    assert 'Base ldapsearch command' in ldap_config_text
1227
    assert 'ldapsearch -v -H ldapi://' in ldap_config_text
1228
    assert '-D "cn=Étienne Michu,o=ôrga" -w "passé" -b "o=ôrga" "(|(mail=*)(uid=*))"' in ldap_config_text
1229

  
1230
    for opt in [
1231
        'active_directory',
1232
        'attribute_mappings',
1233
        'attributes',
1234
        'basedn',
1235
        'bind_with_username',
1236
        'binddn',
1237
        'bindpw',
1238
        'bindsasl',
1239
        'cacertdir',
1240
        'cacertfile',
1241
        'can_reset_password',
1242
        'certfile',
1243
        'clean_external_id_on_update',
1244
        'connect_with_user_credentials',
1245
        'create_group',
1246
        'disable_update',
1247
        'email_field',
1248
        'external_id_tuples',
1249
        'extra_attributes',
1250
        'fname_field',
1251
        'global_ldap_options',
1252
        'group_basedn',
1253
        'group_filter',
1254
        'group_mapping',
1255
        'group_to_role_mapping',
1256
        'groupactive',
1257
        'groupstaff',
1258
        'groupsu',
1259
        'is_staff',
1260
        'is_superuser',
1261
        'keep_password',
1262
        'keep_password_in_session',
1263
        'keyfile',
1264
        'ldap_options',
1265
        'ldap_uri',
1266
        'limit_to_realm',
1267
        'lname_field',
1268
        'lookups',
1269
        'mandatory_attributes_values',
1270
        'member_of_attribute',
1271
        'multimatch',
1272
        'ou_slug',
1273
        'ppolicy_dn',
1274
        'realm',
1275
        'referrals',
1276
        'replicas',
1277
        'require_cert',
1278
        'set_mandatory_groups',
1279
        'set_mandatory_roles',
1280
        'shuffle_replicas',
1281
        'sync_ldap_users_filter',
1282
        'timeout',
1283
        'update_username',
1284
        'url',
1285
        'use_controls',
1286
        'use_first_url_for_external_id',
1287
        'use_password_modify',
1288
        'use_tls',
1289
        'user_attributes',
1290
        'user_basedn',
1291
        'user_can_change_password',
1292
        'user_dn_template',
1293
        'user_filter',
1294
        'username_template',
1295
    ]:
1296
        assert opt in ldap_config_text
1297

  
1298
    # mock a buggy connection
1299
    monkeypatch.setattr(ldap_backend.LDAPBackend, 'get_connection', lambda x: None)
1300
    resp = app.get(reverse('a2-manager-tech-info'))
1301
    ldap_config_text = resp.pyquery('div#a2-manager-tech-info-ldap-list').text()
1302

  
1303
    assert 'Base ldapsearch command' not in ldap_config_text
1304
    assert 'Error while attempting to connect to LDAP server' in ldap_config_text
1200
-