#7 Add PyASN.1 fixes from upstream
Merged 3 years ago by rstrode. Opened 3 years ago by abbra.
rpms/ abbra/python-ldap f32-fixes  into  f32

file added
+157
@@ -0,0 +1,157 @@ 

+ From 8f79376cfcb6327b2ecbd19df715b4a8da52649b Mon Sep 17 00:00:00 2001

+ From: Firstyear <william.brown@suse.com>

+ Date: Thu, 18 Jun 2020 19:03:28 +1000

+ Subject: [PATCH] Syncrepl fails to parse SyncInfoMessage when the message is a

+  syncIdSet

+ 

+ Previously, the SyncInfoMessage parser used a for loop to test for the

+ presence of each named choice with with the structure. However, because

+ refreshDelete and refreshPresent both are able to be fully resolved

+ as defaults, and due to how pyasn1 accesses named types, it was not

+ checking the choice tag, and would return a fully populated refreshDelete

+ to the caller instead.

+ 

+ This fixes the parser to always return the inner component, and retrieves

+ the name based on the current choice of the tagged item.

+ 

+ Author: William Brown <william@blackhats.net.au>

+ 

+ https://github.com/python-ldap/python-ldap/pull/351

+ ---

+  Lib/ldap/syncrepl.py     | 49 ++++++++++++++++++++--------------------

+  Tests/t_ldap_syncrepl.py | 44 +++++++++++++++++++++++++++++++++++-

+  2 files changed, 68 insertions(+), 25 deletions(-)

+ 

+ diff --git a/Lib/ldap/syncrepl.py b/Lib/ldap/syncrepl.py

+ index 0de5cec..f6ac2d1 100644

+ --- a/Lib/ldap/syncrepl.py

+ +++ b/Lib/ldap/syncrepl.py

+ @@ -314,34 +314,35 @@ def __init__(self, encodedMessage):

+          self.refreshPresent = None

+          self.syncIdSet = None

+  

+ -        for attr in ['newcookie', 'refreshDelete', 'refreshPresent', 'syncIdSet']:

+ -            comp = d[0].getComponentByName(attr)

+ -

+ -            if comp is not None and comp.hasValue():

+ -

+ -                if attr == 'newcookie':

+ -                    self.newcookie = str(comp)

+ -                    return

+ +        # Due to the way pyasn1 works, refreshDelete and refreshPresent are both

+ +        # valid in the components as they are fully populated defaults. We must

+ +        # get the component directly from the message, not by iteration.

+ +        attr = d[0].getName()

+ +        comp = d[0].getComponent()

+ +

+ +        if comp is not None and comp.hasValue():

+ +            if attr == 'newcookie':

+ +                self.newcookie = str(comp)

+ +                return

+  

+ -                val = {}

+ +            val = {}

+  

+ -                cookie = comp.getComponentByName('cookie')

+ -                if cookie.hasValue():

+ -                    val['cookie'] = str(cookie)

+ +            cookie = comp.getComponentByName('cookie')

+ +            if cookie.hasValue():

+ +                val['cookie'] = str(cookie)

+  

+ -                if attr.startswith('refresh'):

+ -                    val['refreshDone'] = bool(comp.getComponentByName('refreshDone'))

+ -                elif attr == 'syncIdSet':

+ -                    uuids = []

+ -                    ids = comp.getComponentByName('syncUUIDs')

+ -                    for i in range(len(ids)):

+ -                        uuid = UUID(bytes=bytes(ids.getComponentByPosition(i)))

+ -                        uuids.append(str(uuid))

+ -                    val['syncUUIDs'] = uuids

+ -                    val['refreshDeletes'] = bool(comp.getComponentByName('refreshDeletes'))

+ +            if attr.startswith('refresh'):

+ +                val['refreshDone'] = bool(comp.getComponentByName('refreshDone'))

+ +            elif attr == 'syncIdSet':

+ +                uuids = []

+ +                ids = comp.getComponentByName('syncUUIDs')

+ +                for i in range(len(ids)):

+ +                    uuid = UUID(bytes=bytes(ids.getComponentByPosition(i)))

+ +                    uuids.append(str(uuid))

+ +                val['syncUUIDs'] = uuids

+ +                val['refreshDeletes'] = bool(comp.getComponentByName('refreshDeletes'))

+  

+ -                setattr(self, attr, val)

+ -                return

+ +            setattr(self, attr, val)

+  

+  

+  class SyncreplConsumer:

+ diff --git a/Tests/t_ldap_syncrepl.py b/Tests/t_ldap_syncrepl.py

+ index 9398de5..b8a6ab6 100644

+ --- a/Tests/t_ldap_syncrepl.py

+ +++ b/Tests/t_ldap_syncrepl.py

+ @@ -10,6 +10,7 @@

+  import shelve

+  import sys

+  import unittest

+ +import binascii

+  

+  if sys.version_info[0] <= 2:

+      PY2 = True

+ @@ -21,7 +22,7 @@

+  

+  import ldap

+  from ldap.ldapobject import SimpleLDAPObject

+ -from ldap.syncrepl import SyncreplConsumer

+ +from ldap.syncrepl import SyncreplConsumer, SyncInfoMessage

+  

+  from slapdtest import SlapdObject, SlapdTestCase

+  

+ @@ -445,6 +446,47 @@ def setUp(self):

+          )

+          self.suffix = self.server.suffix.encode('utf-8')

+  

+ +class DecodeSyncreplProtoTests(unittest.TestCase):

+ +    """

+ +    Tests of the ASN.1 decoder for tricky cases or past issues to ensure that

+ +    syncrepl messages are handled correctly.

+ +    """

+ +

+ +    def test_syncidset_message(self):

+ +        """

+ +        A syncrepl server may send a sync info message, with a syncIdSet

+ +        of uuids to delete. A regression was found in the original

+ +        sync info message implementation due to how the choice was

+ +        evaluated, because refreshPresent and refreshDelete were both

+ +        able to be fully expressed as defaults, causing the parser

+ +        to mistakenly catch a syncIdSet as a refreshPresent/refereshDelete.

+ +

+ +        This tests that a syncIdSet request is properly decoded.

+ +

+ +        reference: https://tools.ietf.org/html/rfc4533#section-2.5

+ +        """

+ +

+ +        # This is a dump of a syncidset message from wireshark + 389-ds

+ +        msg = """

+ +        a36b04526c6461706b64632e6578616d706c652e636f6d3a333839303123636e

+ +        3d6469726563746f7279206d616e616765723a64633d6578616d706c652c6463

+ +        3d636f6d3a286f626a656374436c6173733d2a2923330101ff311204108dc446

+ +        01a93611ea8aaff248c5fa5780

+ +        """.replace(' ', '').replace('\n', '')

+ +

+ +        msgraw = binascii.unhexlify(msg)

+ +        sim = SyncInfoMessage(msgraw)

+ +        self.assertEqual(sim.refreshDelete, None)

+ +        self.assertEqual(sim.refreshPresent, None)

+ +        self.assertEqual(sim.newcookie, None)

+ +        self.assertEqual(sim.syncIdSet,

+ +            {

+ +                'cookie': 'ldapkdc.example.com:38901#cn=directory manager:dc=example,dc=com:(objectClass=*)#3',

+ +                'syncUUIDs': ['8dc44601-a936-11ea-8aaf-f248c5fa5780'],

+ +                'refreshDeletes': True

+ +            }

+ +        )

+ +

+  

+  if __name__ == '__main__':

+      unittest.main()

file modified
+7 -1
@@ -12,11 +12,12 @@ 

  

  Name: python-ldap

  Version: 3.1.0

- Release: 9%{?dist}

+ Release: 10%{?dist}

  License: Python

  Summary: An object-oriented API to access LDAP directory servers

  URL: http://python-ldap.org/

  Source0: https://files.pythonhosted.org/packages/source/p/%{name}/%{name}-%{version}%{?prerelease}.tar.gz

+ Patch1: pyasn1-fixes.patch

  

  ### Build Dependencies ###

  BuildRequires: gcc
@@ -59,6 +60,8 @@ 

  

  %prep

  %setup -q -n %{name}-%{version}%{?prerelease}

+ %patch1 -p1

+ 

  # Fix interpreter

  find . -name '*.py' | xargs sed -i '1s|^#!/usr/bin/env python|#!%{__python3}|'

  
@@ -93,6 +96,9 @@ 

  %{python3_sitearch}/python_ldap-%{version}%{?prerelease}-py%{python3_version}.egg-info/

  

  %changelog

+ * Tue Oct 13 2020 Alexander Bokovoy <abokovoy@redhat.com> - 3.1.0-10

+ - Add PyASN.1 fixes from https://github.com/python-ldap/python-ldap/pull/351

+ 

  * Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.1.0-9

  - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild

  

This is a fix from https://github.com/python-ldap/python-ldap/pull/351 which is required for syncrepl use in FreeIPA. It would be great to merge this to f32-f34 branches.

@johnp @rstrode, could you please merge this change?

Any update? Do we have active maintainers in this project or should I initiate a process to take the package over?

Pull-Request has been merged by rstrode

3 years ago

you should definitely initiate the process to take the package over. there's no way J5 still maintains this.

(and I have no idea why i'm on the commit acl for it, other than maybe it was some desktop dependency at some point)