diff options
author | Fraser Tweedale <ftweedal@redhat.com> | 2016-10-10 16:08:52 +1000 |
---|---|---|
committer | David Kupka <dkupka@redhat.com> | 2016-11-10 10:21:47 +0100 |
commit | 9522970bfa28900abc90e959de483f59c79a3e5f (patch) | |
tree | 421ead9477447aacefe7de33e078275b5a6c4175 | |
parent | 9bb6d8643f4eb7214897de28821839a14a3bcb37 (diff) | |
download | freeipa-9522970bfa28900abc90e959de483f59c79a3e5f.tar.gz freeipa-9522970bfa28900abc90e959de483f59c79a3e5f.tar.xz freeipa-9522970bfa28900abc90e959de483f59c79a3e5f.zip |
dn: support conversion from python-cryptography Name
The upcoming change to using python-cryptography for certificate
process will require a way to convert
``cryptography.x509.name.Name`` values to ``ipapython.dn.DN``.
Update the ``DN`` constructor to accept a ``Name``.
Part of: https://fedorahosted.org/freeipa/ticket/6398
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
-rw-r--r-- | ipapython/dn.py | 44 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_dn.py | 23 |
2 files changed, 63 insertions, 4 deletions
diff --git a/ipapython/dn.py b/ipapython/dn.py index 3ee35c626..2f7655d27 100644 --- a/ipapython/dn.py +++ b/ipapython/dn.py @@ -422,6 +422,7 @@ from __future__ import print_function import sys import functools +import cryptography.x509 from ldap.dn import str2dn, dn2str from ldap import DECODING_ERROR import six @@ -976,6 +977,8 @@ class DN(object): to yield one or more RDN's which will be appended in order to the DN. The parsing recognizes the DN syntax escaping rules. + * A single ``cryptography.x509.name.Name`` object. + * A RDN object, the RDN will copied respecting the constructors keyword configuration parameters and appended in order. @@ -1125,9 +1128,17 @@ class DN(object): rdns = [[ava]] elif isinstance(value, RDN): rdns = [value.to_openldap()] + elif isinstance(value, cryptography.x509.name.Name): + rdns = list(reversed([ + [get_ava( + _ATTR_NAME_BY_OID.get(ava.oid, ava.oid.dotted_string), + ava.value)] + for ava in value + ])) else: - raise TypeError("must be str, unicode, tuple, or RDN or DN, got %s instead" % - type(value)) + raise TypeError( + "must be str, unicode, tuple, Name, RDN or DN, got %s instead" + % type(value)) return rdns def _rdns_from_sequence(self, seq): @@ -1407,3 +1418,32 @@ class DN(object): if i == -1: raise ValueError("pattern not found") return i + + +_ATTR_NAME_BY_OID = { + cryptography.x509.oid.NameOID.COMMON_NAME: 'CN', + cryptography.x509.oid.NameOID.COUNTRY_NAME: 'C', + cryptography.x509.oid.NameOID.LOCALITY_NAME: 'L', + cryptography.x509.oid.NameOID.STATE_OR_PROVINCE_NAME: 'ST', + cryptography.x509.oid.NameOID.ORGANIZATION_NAME: 'O', + cryptography.x509.oid.NameOID.ORGANIZATIONAL_UNIT_NAME: 'OU', + cryptography.x509.oid.NameOID.SERIAL_NUMBER: 'serialNumber', + cryptography.x509.oid.NameOID.SURNAME: 'SN', + cryptography.x509.oid.NameOID.GIVEN_NAME: 'givenName', + cryptography.x509.oid.NameOID.TITLE: 'title', + cryptography.x509.oid.NameOID.GENERATION_QUALIFIER: 'generationQualifier', + cryptography.x509.oid.NameOID.DN_QUALIFIER: 'dnQualifier', + cryptography.x509.oid.NameOID.PSEUDONYM: 'pseudonym', + cryptography.x509.oid.NameOID.DOMAIN_COMPONENT: 'DC', + cryptography.x509.oid.NameOID.EMAIL_ADDRESS: 'E', + cryptography.x509.oid.NameOID.JURISDICTION_COUNTRY_NAME: + 'incorporationCountry', + cryptography.x509.oid.NameOID.JURISDICTION_LOCALITY_NAME: + 'incorporationLocality', + cryptography.x509.oid.NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: + 'incorporationState', + cryptography.x509.oid.NameOID.BUSINESS_CATEGORY: 'businessCategory', + cryptography.x509.ObjectIdentifier('2.5.4.9'): 'STREET', + cryptography.x509.ObjectIdentifier('2.5.4.17'): 'postalCode', + cryptography.x509.ObjectIdentifier('0.9.2342.19200300.100.1.1'): 'UID', +} diff --git a/ipatests/test_ipapython/test_dn.py b/ipatests/test_ipapython/test_dn.py index a96bd33c5..3ca3b570a 100644 --- a/ipatests/test_ipapython/test_dn.py +++ b/ipatests/test_ipapython/test_dn.py @@ -2,6 +2,7 @@ import contextlib import unittest import pytest +from cryptography import x509 import six from ipapython.dn import DN, RDN, AVA @@ -621,7 +622,7 @@ class TestDN(unittest.TestCase): def setUp(self): # ava1 must sort before ava2 self.attr1 = 'cn' - self.value1 = 'Bob' + self.value1 = u'Bob' self.str_ava1 = '%s=%s' % (self.attr1, self.value1) self.ava1 = AVA(self.attr1, self.value1) @@ -629,7 +630,7 @@ class TestDN(unittest.TestCase): self.rdn1 = RDN((self.attr1, self.value1)) self.attr2 = 'ou' - self.value2 = 'people' + self.value2 = u'people' self.str_ava2 = '%s=%s' % (self.attr2, self.value2) self.ava2 = AVA(self.attr2, self.value2) @@ -656,6 +657,11 @@ class TestDN(unittest.TestCase): self.base_container_dn = DN((self.attr1, self.value1), self.container_dn, self.base_dn) + self.x500name = x509.Name([ + x509.NameAttribute( + x509.NameOID.ORGANIZATIONAL_UNIT_NAME, self.value2), + x509.NameAttribute(x509.NameOID.COMMON_NAME, self.value1), + ]) def assertExpectedClass(self, klass, obj, component): self.assertIs(obj.__class__, expected_class(klass, component)) @@ -794,6 +800,19 @@ class TestDN(unittest.TestCase): self.assertEqual(dn1[0], self.rdn1) self.assertEqual(dn1[1], self.rdn2) + # Create with a python-cryptography 'Name' + dn1 = DN(self.x500name) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[1], self.rdn2) + # Create with RDN, and 2 DN's (e.g. attr + container + base) dn1 = DN((self.attr1, self.value1), self.container_dn, self.base_dn) self.assertEqual(len(dn1), 5) |