diff options
-rw-r--r-- | CHANGELOG.rst | 3 | ||||
-rw-r--r-- | docs/x509/reference.rst | 5 | ||||
-rw-r--r-- | src/cryptography/x509/name.py | 11 | ||||
-rw-r--r-- | tests/x509/test_x509.py | 15 |
4 files changed, 23 insertions, 11 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c3a415c3..70be0520 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,9 @@ Changelog ``cryptography`` release. * Fixed multiple issues preventing ``cryptography`` from compiling against LibreSSL 2.7.x. +* The :class:`~cryptography.x509.RelativeDistinguishedName` class now + preserves the order of attributes. Duplicate attributes now raise an error + instead of silently discarding duplicates. .. _v2-2-2: diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 3fc6507e..64097bf2 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -1117,7 +1117,7 @@ X.509 CSR (Certificate Signing Request) Builder Object Technically, a Name is a list of *sets* of attributes, called *Relative Distinguished Names* or *RDNs*, although multi-valued RDNs are rarely encountered. The iteration order of values within a multi-valued RDN is - undefined. If you need to handle multi-valued RDNs, the ``rdns`` property + preserved. If you need to handle multi-valued RDNs, the ``rdns`` property gives access to an ordered list of :class:`RelativeDistinguishedName` objects. @@ -1203,7 +1203,8 @@ X.509 CSR (Certificate Signing Request) Builder Object .. versionadded:: 1.6 A relative distinguished name is a non-empty set of name attributes. The - object is iterable to get every attribute. + object is iterable to get every attribute, preserving the original order. + Passing duplicate attributes to the constructor raises ``ValueError``. .. method:: get_attributes_for_oid(oid) diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py index 0daa8bbd..5548eda8 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -101,13 +101,18 @@ class NameAttribute(object): class RelativeDistinguishedName(object): def __init__(self, attributes): - attributes = frozenset(attributes) + attributes = list(attributes) if not attributes: raise ValueError("a relative distinguished name cannot be empty") if not all(isinstance(x, NameAttribute) for x in attributes): raise TypeError("attributes must be an iterable of NameAttribute") + # Keep list and frozenset to preserve attribute order where it matters self._attributes = attributes + self._attribute_set = frozenset(attributes) + + if len(self._attribute_set) != len(attributes): + raise ValueError("duplicate attributes are not allowed") def get_attributes_for_oid(self, oid): return [i for i in self if i.oid == oid] @@ -116,13 +121,13 @@ class RelativeDistinguishedName(object): if not isinstance(other, RelativeDistinguishedName): return NotImplemented - return self._attributes == other._attributes + return self._attribute_set == other._attribute_set def __ne__(self, other): return not self == other def __hash__(self): - return hash(self._attributes) + return hash(self._attribute_set) def __iter__(self): return iter(self._attributes) diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 335a0fbf..7f9f1830 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -3886,11 +3886,11 @@ class TestRelativeDistinguishedName(object): x509.RelativeDistinguishedName(["not-a-NameAttribute"]) def test_init_duplicate_attribute(self): - rdn = x509.RelativeDistinguishedName([ - x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), - ]) - assert len(rdn) == 1 + with pytest.raises(ValueError): + x509.RelativeDistinguishedName([ + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'val1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'val1'), + ]) def test_hash(self): rdn1 = x509.RelativeDistinguishedName([ @@ -3932,8 +3932,11 @@ class TestRelativeDistinguishedName(object): assert rdn1 != object() def test_iter_input(self): + # Order must be preserved too attrs = [ - x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1') + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value3') ] rdn = x509.RelativeDistinguishedName(iter(attrs)) assert list(rdn) == attrs |