Projet

Général

Profil

0001-ldap-return-server-error-info-on-failed-connection-a.patch

Paul Marillonnet, 17 juin 2022 12:09

Télécharger (9,3 ko)

Voir les différences:

Subject: [PATCH 1/2] ldap: return server error info on failed connection
 attempts (#65491)

 src/authentic2/backends/ldap_backend.py | 72 +++++++++++++------------
 src/authentic2/manager/views.py         |  2 +-
 tests/test_ldap.py                      |  5 +-
 3 files changed, 41 insertions(+), 38 deletions(-)
src/authentic2/backends/ldap_backend.py
362 362
            return None
363 363

  
364 364
    def check_password(self, raw_password):
365
        connection = self.ldap_backend.get_connection(self.block)
365
        connection, _ = self.ldap_backend.get_connection(self.block)
366 366
        if connection:
367 367
            try:
368 368
                connection.simple_bind_s(self.dn, raw_password)
......
380 380
        # if the verify pass, we have the old password stored in self._current_password
381 381
        _current_password = getattr(self, '_current_password', None) or self.get_password_in_session()
382 382
        if _current_password != new_password:
383
            conn = self.get_connection()
383
            conn, dummy = self.get_connection()
384 384
            if not conn:
385 385
                log.warning('ldap: set_password failed, could not get a connection')
386 386
                return
......
414 414
        cache_key = hashlib.md5(
415 415
            (force_text(str(self.pk)) + ';' + force_text(self.dn)).encode('utf-8')
416 416
        ).hexdigest()
417
        conn = self.get_connection()
417
        conn, dummy = self.get_connection()
418 418
        # prevents blocking on temporary LDAP failures
419 419
        if conn is not None:
420 420
            attributes = self.ldap_backend.get_ldap_attributes(self.block, conn, self.dn) or {}
......
599 599
        group_to_role_mapping = block.get('group_to_role_mapping')
600 600
        if not block.get('group_filter') or not group_to_role_mapping:
601 601
            return
602
        for conn in cls.get_connections(block):
602
        for conn, dummy in cls.get_connections(block):
603
            if not conn:
604
                continue
603 605
            existing_groups = cls.get_groups_dns(conn, block)
604 606
            for group_dn, dummy_role_slugs in group_to_role_mapping:
605 607
                if group_dn in existing_groups:
......
647 649
                return user
648 650

  
649 651
    def authenticate_block(self, request, block, username, password):
650
        for conn in self.get_connections(block):
652
        for conn, dummy in self.get_connections(block):
653
            if not conn:
654
                continue
651 655
            ldap_uri = conn.get_option(ldap.OPT_URI)
652 656
            authz_ids = []
653 657
            user_basedn = force_text(block.get('user_basedn') or block['basedn'])
......
1628 1632
    @classmethod
1629 1633
    def get_users_for_block(cls, block):
1630 1634
        log.info('Synchronising users from realm "%s"', block['realm'])
1631
        conn = cls.get_connection(block)
1635
        conn, dummy = cls.get_connection(block)
1632 1636
        if conn is None:
1633 1637
            log.warning('unable to synchronize with LDAP servers %s', force_text(block['url']))
1634 1638
            return
......
1682 1686
        from authentic2.manager.journal_event_types import ManagerUserDeactivation
1683 1687

  
1684 1688
        for block in cls.get_config():
1685
            conn = cls.get_connection(block)
1689
            conn, dummy = cls.get_connection(block)
1686 1690
            if conn is None:
1687 1691
                continue
1688 1692
            ldap_uri = conn.get_option(ldap.OPT_URI)
......
1810 1814
            conn.set_option(ldap.OPT_REFERRALS, 1 if block['referrals'] else 0)
1811 1815
            # allow TLS options to be applied
1812 1816
            conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
1817
            error = None
1813 1818
            try:
1814 1819
                if not url.startswith('ldaps://') and block['use_tls']:
1815 1820
                    try:
1816 1821
                        conn.start_tls_s()
1817 1822
                    except ldap.CONNECT_ERROR:
1818
                        log.error(
1819
                            'connection to %r failed when activating TLS, did you forget to declare the TLS'
1820
                            ' certificate in /etc/ldap/ldap.conf ?',
1821
                            url,
1822
                        )
1823
                        continue
1823
                        error = (
1824
                            'connection to %r failed when activating TLS, did you forget to declare the TLS certificate'
1825
                            ' in /etc/ldap/ldap.conf ?'
1826
                        ) % url
1824 1827
            except ldap.TIMEOUT:
1825
                log.error('connection to %r timed out', url)
1826
                continue
1828
                error = 'connection to %r timed out' % url
1827 1829
            except ldap.CONNECT_ERROR:
1828
                log.error(
1830
                error = (
1829 1831
                    'connection to %r failed when activating TLS, did you forget to declare the TLS'
1830
                    ' certificate in /etc/ldap/ldap.conf ?',
1831
                    url,
1832
                )
1833
                continue
1832
                    ' certificate in /etc/ldap/ldap.conf ?'
1833
                ) % url
1834 1834
            except ldap.SERVER_DOWN:
1835
                if block['replicas']:
1836
                    log.warning('ldap %r is down', url)
1837
                else:
1838
                    log.error('ldap %r is down', url)
1839
                continue
1835
                error = 'ldap %r is down' % url
1840 1836
            user_credentials = block['connect_with_user_credentials'] and credentials
1841
            success, error = cls.bind(block, conn, credentials=user_credentials)
1842
            if success:
1843
                yield conn
1837
            if error:
1838
                log.error(error)
1839
                yield None, error
1844 1840
            else:
1845
                if block['replicas']:
1846
                    log.warning('admin bind failed on %s: %s', url, error)
1841
                success, binderror = cls.bind(block, conn, credentials=user_credentials)
1842
                if success:
1843
                    yield conn, None
1847 1844
                else:
1848
                    log.error('admin bind failed on %s: %s', url, error)
1845
                    msg = 'admin bind failed on %s: %s' % (url, binderror)
1846
                    yield None, error
1847
                    if block['replicas']:
1848
                        log.warning(msg)
1849
                    else:
1850
                        log.error(msg)
1849 1851

  
1850 1852
    @classmethod
1851 1853
    def bind(cls, block, conn, credentials=()):
......
1903 1905
    @classmethod
1904 1906
    def get_connection(cls, block, credentials=()):
1905 1907
        '''Try to get at least one connection'''
1906
        for conn in cls.get_connections(block, credentials=credentials):
1907
            return conn
1908
        return None
1908
        for conn, err in cls.get_connections(block, credentials=credentials):
1909
            return conn, err
1910
        return None, None
1909 1911

  
1910 1912
    @classmethod
1911 1913
    def update_default(cls, block, validate=True):
......
2015 2017
                if user_external_id.source != force_text(block['realm']):
2016 2018
                    continue
2017 2019
                for external_id_tuple in map_text(block['external_id_tuples']):
2018
                    conn = self.ldap_backend.get_connection(block)
2020
                    conn, dummy = self.ldap_backend.get_connection(block)
2019 2021
                    if not conn:
2020 2022
                        log.warning('ldap: password-lost authenticate failed, could not get a connection')
2021 2023
                        continue
src/authentic2/manager/views.py
676 676
        kwargs['ldap_list'] = []
677 677
        for block in backend.get_config():
678 678
            config = block.copy()
679
            conn = backend.get_connection(config)
679
            conn, dummy = backend.get_connection(config)
680 680
            if not conn:
681 681
                kwargs['error'] = True
682 682
            else:
tests/test_ldap.py
1532 1532

  
1533 1533
    time.sleep(pwdMaxAge * 3)
1534 1534

  
1535
    conn = ldap_backend.LDAPBackend.get_connection(settings.LDAP_AUTH_SETTINGS[0])
1535
    conn, errmsg = ldap_backend.LDAPBackend.get_connection(settings.LDAP_AUTH_SETTINGS[0])
1536
    assert errmsg is None
1536 1537
    attributes = ldap_backend.LDAPBackend.get_ppolicy_attributes(settings.LDAP_AUTH_SETTINGS[0], conn, DN)
1537 1538
    assert 'pwdchangedtime' in attributes
1538 1539
    assert attributes['pwdmaxage'] == [str(pwdMaxAge)]
......
2288 2289
        assert opt in ldap_config_text
2289 2290

  
2290 2291
    # mock a buggy connection
2291
    monkeypatch.setattr(ldap_backend.LDAPBackend, 'get_connection', lambda x: None)
2292
    monkeypatch.setattr(ldap_backend.LDAPBackend, 'get_connection', lambda x: (None, None))
2292 2293
    resp = app.get(reverse('a2-manager-tech-info'))
2293 2294
    ldap_config_text = resp.pyquery('div#a2-manager-tech-info-ldap-list').text()
2294 2295

  
2295
-