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>
This commit is contained in:
Fraser Tweedale 2016-10-10 16:08:52 +10:00 committed by David Kupka
parent 9bb6d8643f
commit 9522970bfa
2 changed files with 63 additions and 4 deletions

View File

@ -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',
}

View File

@ -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)