Source code for truepy._name

# coding: utf-8
# truepy
# Copyright (C) 2014-2015 Moses Palmér
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.

import cryptography.x509
import re

from ._bean import bean_serializer, value_to_xml
from ._bean_serializers import bean_class


@bean_class('javax.security.auth.x500.X500Principal')
[docs]class Name(list): #: The escapable characters ESCAPABLES = ('"', '+', ',', ';', '<', '>') #: A mapping from short attribute names to OIDs ATTRIBUTES = { 'C': cryptography.x509.OID_COUNTRY_NAME, 'O': cryptography.x509.OID_ORGANIZATION_NAME, 'OU': cryptography.x509.OID_ORGANIZATIONAL_UNIT_NAME, 'ST': cryptography.x509.OID_STATE_OR_PROVINCE_NAME, 'CN': cryptography.x509.OID_COMMON_NAME, 'L': cryptography.x509.OID_LOCALITY_NAME, 'SN': cryptography.x509.OID_SURNAME, 'GN': cryptography.x509.OID_GIVEN_NAME} #: The reversed mapping from `attr`:ATTRIBUTES REVERSED_ATTRIBUTES = { value: key for key, value in ATTRIBUTES.items()} SUB_RE = re.compile(r'\#([0-9a-fA-F]{2})') @classmethod def _bean_deserialize(self, element): return self(element.find('.//string').text) @classmethod
[docs] def escape(self, s): """Escapes a string. :param str s: The string to escape. :return: an escaped string :rtype: str """ return ''.join( '#%02X' % ord(c) if c in self.ESCAPABLES else c for c in s)
@classmethod
[docs] def unescape(self, s): """Unescapes a string. This is the inverse operation to escape. :param str s: The string to unescape. :return: an unescaped string :rtype: str :raises ValueError: if an invalid escape is encountered; only characters in :attr:`ESCAPABLES` are supported """ def replacer(m): char = chr(int(m.group(1), 16)) if char not in self.ESCAPABLES: raise ValueError('invalid escape sequence: %s', m.group(0)) return char return self.SUB_RE.sub(replacer, s)
def __init__(self, name): """A class representing a simplified version of an X500 name. The string must be on the form ``'<type>=<value>[,<type>=<value,...]'``. No escapes for ``<type>`` are supported, and only ``DQUOTE``, ``PLUS``, ``COMMA``, ``SEMI``, ``LANGLE`` and ``RANGLE`` are supported for ``<value>``. Leading and trailing space is stripped for the value. :param name: The *X.509* name string from which to create this instance. This may also be a list of the tuple ``(type, value)``. :type name: str or list :raises ValueError: if any part contains an invalid escape sequence, or any part does not contain an ``'='`` """ if isinstance(name, list): self.extend(name) else: try: self.extend( ( kv.split('=')[0].strip(), self.unescape(kv.split('=')[1].strip())) for kv in name.split(',')) except IndexError: raise ValueError('invalid X509 name: %s', name) def __str__(self): return ','.join( '%s=%s' % (k, self.escape(v)) for (k, v) in self) @classmethod
[docs] def from_x509_name(self, name): """Creates a name from a :class:`cryptography.x509.Name`. :param cryptography.x509.Name name: The source name. """ return self([ (self.REVERSED_ATTRIBUTES[na.oid], na.value) for na in name])
@bean_serializer(Name) def name_serializer(v): """Serialises a truepy.Name instance to a javax.security.auth.x500.X500Principal""" return value_to_xml( str(v), 'string', Name.bean_class)