From e9f16176f2a74308a7f8892c5c1bc2b53edad678 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal@redhat.com>
Date: Wed, 29 May 2019 13:22:35 +1000
Subject: [PATCH] dn: sort AVAs when converting from x509.Name

Equal DNs with multi-valued RDNs can compare inequal if one (or
both) is constructed from a cryptography.x509.Name, because the AVAs
in the multi-valued RDNs are not being sorted.

Sort the AVAs when constructing from Name and add test cases for
equality checks on multi-valued RDNs constructed from inputs with
permuted AVA order.

Part of: https://pagure.io/freeipa/issue/7963
---
 ipapython/dn.py                    |  2 ++
 ipatests/test_ipapython/test_dn.py | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/ipapython/dn.py b/ipapython/dn.py
index fda28a3f9c..aa15c19ca7 100644
--- a/ipapython/dn.py
+++ b/ipapython/dn.py
@@ -1141,6 +1141,8 @@ def _rdns_from_value(self, value):
                     ava.value) for ava in rdn]
                 for rdn in value.rdns
             ]))
+            for rdn in rdns:
+                sort_avas(rdn)
         else:
             raise TypeError(
                 "must be str, unicode, tuple, Name, RDN or DN, got %s instead"
diff --git a/ipatests/test_ipapython/test_dn.py b/ipatests/test_ipapython/test_dn.py
index a21abd69b1..7e3f8cb02d 100644
--- a/ipatests/test_ipapython/test_dn.py
+++ b/ipatests/test_ipapython/test_dn.py
@@ -672,6 +672,10 @@ def setUp(self):
             x509.RelativeDistinguishedName([c, st]),
             x509.RelativeDistinguishedName([cn]),
         ])
+        self.x500nameMultiRDN2 = x509.Name([
+            x509.RelativeDistinguishedName([st, c]),
+            x509.RelativeDistinguishedName([cn]),
+        ])
 
     def assertExpectedClass(self, klass, obj, component):
         self.assertIs(obj.__class__, expected_class(klass, component))
@@ -946,6 +950,23 @@ def test_cmp(self):
 
         self.assertFalse(self.container_rdn1 in self.base_dn)
 
+    def test_eq_multi_rdn(self):
+        dn1 = DN(self.ava1, 'ST=Queensland+C=AU')
+        dn2 = DN(self.ava1, 'C=AU+ST=Queensland')
+        self.assertEqual(dn1, dn2)
+
+        # ensure AVAs get sorted when constructing from x509.Name
+        dn3 = DN(self.x500nameMultiRDN)
+        dn4 = DN(self.x500nameMultiRDN2)
+        self.assertEqual(dn3, dn4)
+
+        # ensure AVAs get sorted in the same way regardless of what
+        # the DN was constructed from
+        self.assertEqual(dn1, dn3)
+        self.assertEqual(dn1, dn4)
+        self.assertEqual(dn2, dn3)
+        self.assertEqual(dn2, dn4)
+
     def test_indexing(self):
         dn1 = DN(self.dn1)
         dn2 = DN(self.dn2)
