Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
rfc4512.py851 linesDownload Raw Back to protocol
1"""
2"""
3
4# Created on 2013.09.11
5#
6# Author: Giovanni Cannata
7#
8# Copyright 2013 - 2020 Giovanni Cannata
9#
10# This file is part of ldap3.
11#
12# ldap3 is free software: you can redistribute it and/or modify
13# it under the terms of the GNU Lesser General Public License as published
14# by the Free Software Foundation, either version 3 of the License, or
15# (at your option) any later version.
16#
17# ldap3 is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20# GNU Lesser General Public License for more details.
21#
22# You should have received a copy of the GNU Lesser General Public License
23# along with ldap3 in the COPYING and COPYING.LESSER files.
24# If not, see <http://www.gnu.org/licenses/>.
25
26from os import linesep
27import re
28import json
29
30from .oid import CLASS_ABSTRACT, CLASS_STRUCTURAL, CLASS_AUXILIARY, ATTRIBUTE_USER_APPLICATION, \
31    ATTRIBUTE_DIRECTORY_OPERATION, ATTRIBUTE_DISTRIBUTED_OPERATION, ATTRIBUTE_DSA_OPERATION
32from .. import SEQUENCE_TYPES, STRING_TYPES, get_config_parameter
33from ..utils.conv import escape_bytes, json_hook, check_json_dict, format_json, to_unicode
34from ..utils.ciDict import CaseInsensitiveWithAliasDict
35from ..protocol.formatters.standard import format_attribute_values
36from .oid import Oids, decode_oids, decode_syntax, oid_to_string
37from ..core.exceptions import LDAPSchemaError, LDAPDefinitionError
38
39
40def constant_to_class_kind(value):
41    if value == CLASS_STRUCTURAL:
42        return 'Structural'
43    elif value == CLASS_ABSTRACT:
44        return 'Abstract'
45    elif value == CLASS_AUXILIARY:
46        return 'Auxiliary'
47    else:
48        return '<unknown>'
49
50
51def constant_to_attribute_usage(value):
52    if value == ATTRIBUTE_USER_APPLICATION:
53        return 'User Application'
54    elif value == ATTRIBUTE_DIRECTORY_OPERATION:
55        return "Directory operation"
56    elif value == ATTRIBUTE_DISTRIBUTED_OPERATION:
57        return 'Distributed operation'
58    elif value == ATTRIBUTE_DSA_OPERATION:
59        return 'DSA operation'
60    else:
61        return 'unknown'
62
63
64def attribute_usage_to_constant(value):
65    if value == 'userApplications':
66        return ATTRIBUTE_USER_APPLICATION
67    elif value == 'directoryOperation':
68        return ATTRIBUTE_DIRECTORY_OPERATION
69    elif value == 'distributedOperation':
70        return ATTRIBUTE_DISTRIBUTED_OPERATION
71    elif value == 'dSAOperation':
72        return ATTRIBUTE_DSA_OPERATION
73    else:
74        return 'unknown'
75
76
77def quoted_string_to_list(quoted_string):
78    string = quoted_string.strip()
79    if not string:
80        return list()
81
82    if string[0] == '(' and string[-1] == ')':
83        string = string[1:-1]
84    elements = string.split("'")
85    # return [check_escape(element.strip("'").strip()) for element in elements if element.strip()]
86    return [element.strip("'").strip() for element in elements if element.strip()]
87
88
89def oids_string_to_list(oid_string):
90    string = oid_string.strip()
91    if string[0] == '(' and string[-1] == ')':
92        string = string[1:-1]
93    elements = string.split('$')
94    return [element.strip() for element in elements if element.strip()]
95
96
97def extension_to_tuple(extension_string):
98    string = extension_string.strip()
99    name, _, values = string.partition(' ')
100    return name, quoted_string_to_list(values)
101
102
103def list_to_string(list_object):
104    if not isinstance(list_object, SEQUENCE_TYPES):
105        return list_object
106
107    r = ''
108    for element in list_object:
109        r += (list_to_string(element) if isinstance(element, SEQUENCE_TYPES) else str(element)) + ', '
110
111    return r[:-2] if r else ''
112
113
114class BaseServerInfo(object):
115    def __init__(self, raw_attributes):
116        self.raw = dict(raw_attributes)
117
118    @classmethod
119    def from_json(cls, json_definition, schema=None, custom_formatter=None):
120        conf_case_insensitive_schema = get_config_parameter('CASE_INSENSITIVE_SCHEMA_NAMES')
121        definition = json.loads(json_definition, object_hook=json_hook)
122        if 'raw' not in definition or 'type' not in definition:
123            raise LDAPDefinitionError('invalid JSON definition')
124
125        if conf_case_insensitive_schema:
126            attributes = CaseInsensitiveWithAliasDict()
127        else:
128            attributes = dict()
129
130        if schema:
131            for attribute in definition['raw']:
132                # attributes[attribute] = format_attribute_values(schema, check_escape(attribute), [check_escape(value) for value in definition['raw'][attribute]], custom_formatter)
133                attributes[attribute] = format_attribute_values(schema, attribute, [value for value in definition['raw'][attribute]], custom_formatter)
134        else:
135            for attribute in definition['raw']:
136                # attributes[attribute] = [check_escape(value) for value in definition['raw'][attribute]]
137                attributes[attribute] = [value for value in definition['raw'][attribute]]
138
139        if cls.__name__ != definition['type']:
140            raise LDAPDefinitionError('JSON info not of type ' + cls.__name__)
141
142        if definition['type'] == 'DsaInfo':
143            return DsaInfo(attributes, definition['raw'])
144        elif definition['type'] == 'SchemaInfo':
145            if 'schema_entry' not in definition:
146                raise LDAPDefinitionError('invalid schema in JSON')
147            return SchemaInfo(definition['schema_entry'], attributes, definition['raw'])
148
149        raise LDAPDefinitionError('invalid Info type ' + str(definition['type']) + ' in JSON definition')
150
151    @classmethod
152    def from_file(cls, target, schema=None, custom_formatter=None):
153        if isinstance(target, STRING_TYPES):
154            target = open(target, 'r')
155
156        new = cls.from_json(target.read(), schema=schema, custom_formatter=custom_formatter)
157        target.close()
158        return new
159
160    def to_file(self,
161                target,
162                indent=4,
163                sort=True):
164        if isinstance(target, STRING_TYPES):
165            target = open(target, 'w+')
166
167        target.writelines(self.to_json(indent=indent, sort=sort))
168        target.close()
169
170    def __str__(self):
171        return self.__repr__()
172
173    def to_json(self,
174                indent=4,
175                sort=True):
176        json_dict = dict()
177        json_dict['type'] = self.__class__.__name__
178        json_dict['raw'] = self.raw
179
180        if isinstance(self, SchemaInfo):
181            json_dict['schema_entry'] = self.schema_entry
182        elif isinstance(self, DsaInfo):
183            pass
184        else:
185            raise LDAPDefinitionError('unable to convert ' + str(self) + ' to JSON')
186
187        if str is bytes:  # Python 2
188            check_json_dict(json_dict)
189
190        return json.dumps(json_dict, ensure_ascii=False, sort_keys=sort, indent=indent, check_circular=True, default=format_json, separators=(',', ': '))
191
192
193class DsaInfo(BaseServerInfo):
194    """
195    This class contains info about the ldap server (DSA) read from DSE
196    as defined in RFC4512 and RFC3045. Unknown attributes are stored in the "other" dict
197    """
198
199    def __init__(self, attributes, raw_attributes):
200        BaseServerInfo.__init__(self, raw_attributes)
201        self.alt_servers = attributes.pop('altServer', None)
202        self.naming_contexts = attributes.pop('namingContexts', None)
203        self.supported_controls = decode_oids(attributes.pop('supportedControl', None))
204        self.supported_extensions = decode_oids(attributes.pop('supportedExtension', None))
205        self.supported_features = decode_oids(attributes.pop('supportedFeatures', None)) + decode_oids(attributes.pop('supportedCapabilities', None))
206        self.supported_ldap_versions = attributes.pop('supportedLDAPVersion', None)
207        self.supported_sasl_mechanisms = attributes.pop('supportedSASLMechanisms', None)
208        self.vendor_name = attributes.pop('vendorName', None)
209        self.vendor_version = attributes.pop('vendorVersion', None)
210        self.schema_entry = attributes.pop('subschemaSubentry', None)
211        self.other = attributes  # remaining schema definition attributes not in RFC4512
212
213    def __repr__(self):
214        r = 'DSA info (from DSE):' + linesep
215        if self.supported_ldap_versions:
216            if isinstance(self.supported_ldap_versions, SEQUENCE_TYPES):
217                r += ('  Supported LDAP versions: ' + ', '.join([str(s) for s in self.supported_ldap_versions])) if self.supported_ldap_versions else ''
218            else:
219                r += ('  Supported LDAP versions: ' + str(self.supported_ldap_versions))
220            r += linesep
221        if self.naming_contexts:
222            if isinstance(self.naming_contexts, SEQUENCE_TYPES):
223                r += ('  Naming contexts: ' + linesep + linesep.join(['    ' + str(s) for s in self.naming_contexts])) if self.naming_contexts else ''
224            else:
225                r += ('  Naming contexts: ' + str(self.naming_contexts))
226            r += linesep
227        if self.alt_servers:
228            if isinstance(self.alt_servers, SEQUENCE_TYPES):
229                r += ('  Alternative servers: ' + linesep + linesep.join(['    ' + str(s) for s in self.alt_servers])) if self.alt_servers else ''
230            else:
231                r += ('  Alternative servers: ' + str(self.alt_servers))
232            r += linesep
233        if self.supported_controls:
234            if isinstance(self.supported_controls, SEQUENCE_TYPES):
235                r += ('  Supported controls: ' + linesep + linesep.join(['    ' + oid_to_string(s) for s in self.supported_controls])) if self.supported_controls else ''
236            else:
237                r += ('  Supported controls: ' + str(self.supported_controls))
238            r += linesep
239        if self.supported_extensions:
240            if isinstance(self.supported_extensions, SEQUENCE_TYPES):
241                r += ('  Supported extensions: ' + linesep + linesep.join(['    ' + oid_to_string(s) for s in self.supported_extensions])) if self.supported_extensions else ''
242            else:
243                r += ('  Supported extensions: ' + str(self.supported_extensions))
244            r += linesep
245        if self.supported_features:
246            if self.supported_features:
247                if isinstance(self.supported_features, SEQUENCE_TYPES):
248                    r += ('  Supported features: ' + linesep + linesep.join(['    ' + oid_to_string(s) for s in self.supported_features])) if self.supported_features else ''
249                else:
250                    r += ('  Supported features: ' + str(self.supported_features))
251                r += linesep
252        if self.supported_sasl_mechanisms:
253            if isinstance(self.supported_sasl_mechanisms, SEQUENCE_TYPES):
254                r += ('  Supported SASL mechanisms: ' + linesep + '    ' + ', '.join([str(s) for s in self.supported_sasl_mechanisms])) if self.supported_sasl_mechanisms else ''
255            else:
256                r += ('  Supported SASL mechanisms: ' + str(self.supported_sasl_mechanisms))
257            r += linesep
258        if self.schema_entry:
259            if isinstance(self.schema_entry, SEQUENCE_TYPES):
260                r += ('  Schema entry: ' + linesep + linesep.join(['    ' + str(s) for s in self.schema_entry])) if self.schema_entry else ''
261            else:
262                r += ('  Schema entry: ' + str(self.schema_entry))
263            r += linesep
264        if self.vendor_name:
265            if isinstance(self.vendor_name, SEQUENCE_TYPES) and len(self.vendor_name) == 1:
266                r += 'Vendor name: ' + self.vendor_name[0]
267            else:
268                r += 'Vendor name: ' + str(self.vendor_name)
269            r += linesep
270        if self.vendor_version:
271            if isinstance(self.vendor_version, SEQUENCE_TYPES) and len(self.vendor_version) == 1:
272                r += 'Vendor version: ' + self.vendor_version[0]
273            else:
274                r += 'Vendor version: ' + str(self.vendor_version)
275            r += linesep
276        r += 'Other:' + linesep
277        for k, v in self.other.items():
278            r += '  ' + str(k) + ': ' + linesep
279            try:
280                r += (linesep.join(['    ' + str(s) for s in v])) if isinstance(v, SEQUENCE_TYPES) else str(v)
281            except UnicodeDecodeError:
282                r += (linesep.join(['    ' + str(escape_bytes(s)) for s in v])) if isinstance(v, SEQUENCE_TYPES) else str(escape_bytes(v))
283            r += linesep
284        return r
285
286
287class SchemaInfo(BaseServerInfo):
288    """
289    This class contains info about the ldap server schema read from an entry (default entry is DSE)
290    as defined in RFC4512. Unknown attributes are stored in the "other" dict
291    """
292
293    def __init__(self, schema_entry, attributes, raw_attributes):
294        BaseServerInfo.__init__(self, raw_attributes)
295        self.schema_entry = schema_entry
296        self.create_time_stamp = attributes.pop('createTimestamp', None)
297        self.modify_time_stamp = attributes.pop('modifyTimestamp', None)
298        self.attribute_types = AttributeTypeInfo.from_definition(attributes.pop('attributeTypes', []))
299        self.object_classes = ObjectClassInfo.from_definition(attributes.pop('objectClasses', []))
300        self.matching_rules = MatchingRuleInfo.from_definition(attributes.pop('matchingRules', []))
301        self.matching_rule_uses = MatchingRuleUseInfo.from_definition(attributes.pop('matchingRuleUse', []))
302        self.dit_content_rules = DitContentRuleInfo.from_definition(attributes.pop('dITContentRules', []))
303        self.dit_structure_rules = DitStructureRuleInfo.from_definition(attributes.pop('dITStructureRules', []))
304        self.name_forms = NameFormInfo.from_definition(attributes.pop('nameForms', []))
305        self.ldap_syntaxes = LdapSyntaxInfo.from_definition(attributes.pop('ldapSyntaxes', []))
306        self.other = attributes  # remaining schema definition attributes not in RFC4512
307
308        # links attributes to class objects
309        if self.object_classes and self.attribute_types:
310            for object_class in self.object_classes:  # CaseInsensitiveDict return keys while iterating
311                for attribute in self.object_classes[object_class].must_contain:
312                    try:
313                        self.attribute_types[attribute].mandatory_in.append(object_class)
314                    except KeyError:
315                        pass
316                for attribute in self.object_classes[object_class].may_contain:
317                    try:
318                        self.attribute_types[attribute].optional_in.append(object_class)
319                    except KeyError:
320                        pass
321
322    def is_valid(self):
323        if self.object_classes or self.attribute_types or self.matching_rules or self.matching_rule_uses or self.dit_content_rules or self.dit_structure_rules or self.name_forms or self.ldap_syntaxes:
324            return True
325        return False
326
327    def __repr__(self):
328        r = 'DSA Schema from: ' + self.schema_entry
329        r += linesep
330        if isinstance(self.attribute_types, SEQUENCE_TYPES):
331            r += ('  Attribute types:' + linesep + '    ' + ', '.join([str(self.attribute_types[s]) for s in self.attribute_types])) if self.attribute_types else ''
332        else:
333            r += ('  Attribute types:' + str(self.attribute_types))
334        r += linesep
335        if isinstance(self.object_classes, SEQUENCE_TYPES):
336            r += ('  Object classes:' + linesep + '    ' + ', '.join([str(self.object_classes[s]) for s in self.object_classes])) if self.object_classes else ''
337        else:
338            r += ('  Object classes:' + str(self.object_classes))
339        r += linesep
340        if isinstance(self.matching_rules, SEQUENCE_TYPES):
341            r += ('  Matching rules:' + linesep + '    ' + ', '.join([str(self.matching_rules[s]) for s in self.matching_rules])) if self.matching_rules else ''
342        else:
343            r += ('  Matching rules:' + str(self.matching_rules))
344        r += linesep
345        if isinstance(self.matching_rule_uses, SEQUENCE_TYPES):
346            r += ('  Matching rule uses:' + linesep + '    ' + ', '.join([str(self.matching_rule_uses[s]) for s in self.matching_rule_uses])) if self.matching_rule_uses else ''
347        else:
348            r += ('  Matching rule uses:' + str(self.matching_rule_uses))
349        r += linesep
350        if isinstance(self.dit_content_rules, SEQUENCE_TYPES):
351            r += ('  DIT content rules:' + linesep + '    ' + ', '.join([str(self.dit_content_rules[s]) for s in self.dit_content_rules])) if self.dit_content_rules else ''
352        else:
353            r += ('  DIT content rules:' + str(self.dit_content_rules))
354        r += linesep
355        if isinstance(self.dit_structure_rules, SEQUENCE_TYPES):
356            r += ('  DIT structure rules:' + linesep + '    ' + ', '.join([str(self.dit_structure_rules[s]) for s in self.dit_structure_rules])) if self.dit_structure_rules else ''
357        else:
358            r += ('  DIT structure rules:' + str(self.dit_structure_rules))
359        r += linesep
360        if isinstance(self.name_forms, SEQUENCE_TYPES):
361            r += ('  Name forms:' + linesep + '    ' + ', '.join([str(self.name_forms[s]) for s in self.name_forms])) if self.name_forms else ''
362        else:
363            r += ('  Name forms:' + str(self.name_forms))
364        r += linesep
365        if isinstance(self.ldap_syntaxes, SEQUENCE_TYPES):
366            r += ('  LDAP syntaxes:' + linesep + '    ' + ', '.join([str(self.ldap_syntaxes[s]) for s in self.ldap_syntaxes])) if self.ldap_syntaxes else ''
367        else:
368            r += ('  LDAP syntaxes:' + str(self.ldap_syntaxes))
369        r += linesep
370        r += 'Other:' + linesep
371
372        for k, v in self.other.items():
373            r += '  ' + str(k) + ': ' + linesep
374            try:
375                r += (linesep.join(['    ' + str(s) for s in v])) if isinstance(v, SEQUENCE_TYPES) else str(v)
376            except UnicodeDecodeError:
377                r += (linesep.join(['    ' + str(escape_bytes(s)) for s in v])) if isinstance(v, SEQUENCE_TYPES) else str(escape_bytes(v))
378            r += linesep
379        return r
380
381
382class BaseObjectInfo(object):
383    """
384    Base class for objects defined in the schema as per RFC4512
385    """
386
387    def __init__(self,
388                 oid=None,
389                 name=None,
390                 description=None,
391                 obsolete=False,
392                 extensions=None,
393                 experimental=None,
394                 definition=None):
395
396        self.oid = oid
397        self.name = name
398        self.description = description
399        self.obsolete = obsolete
400        self.extensions = extensions
401        self.experimental = experimental
402        self.raw_definition = definition
403        self._oid_info = None
404
405    @property
406    def oid_info(self):
407        if self._oid_info is None and self.oid:
408            self._oid_info = Oids.get(self.oid, '')
409
410        return self._oid_info if self._oid_info else None
411
412    def __str__(self):
413        return self.__repr__()
414
415    def __repr__(self):
416        r = ': ' + self.oid
417        r += ' [OBSOLETE]' if self.obsolete else ''
418        r += (linesep + '  Short name: ' + list_to_string(self.name)) if self.name else ''
419        r += (linesep + '  Description: ' + self.description) if self.description else ''
420        r += '<__desc__>'
421        r += (linesep + '  Extensions:' + linesep + linesep.join(['    ' + s[0] + ': ' + list_to_string(s[1]) for s in self.extensions])) if self.extensions else ''
422        r += (linesep + '  Experimental:' + linesep + linesep.join(['    ' + s[0] + ': ' + list_to_string(s[1]) for s in self.experimental])) if self.experimental else ''
423        r += (linesep + '  OidInfo: ' + str(self.oid_info)) if self.oid_info else ''
424        r += linesep
425        return r
426
427    @classmethod
428    def from_definition(cls, definitions):
429        conf_case_insensitive_schema = get_config_parameter('CASE_INSENSITIVE_SCHEMA_NAMES')
430        conf_ignore_malformed_schema = get_config_parameter('IGNORE_MALFORMED_SCHEMA')
431
432        ret_dict = CaseInsensitiveWithAliasDict() if conf_case_insensitive_schema else dict()
433
434        if not definitions:
435            return ret_dict
436
437        for object_definition in definitions:
438            object_definition = to_unicode(object_definition.strip(), from_server=True)
439            if object_definition[0] == '(' and object_definition[-1] == ')':
440                if cls is MatchingRuleInfo:
441                    pattern = '| SYNTAX '
442                elif cls is ObjectClassInfo:
443                    pattern = '| SUP | ABSTRACT| STRUCTURAL| AUXILIARY| MUST | MAY '
444                elif cls is AttributeTypeInfo:
445                    pattern = '| SUP | EQUALITY | ORDERING | SUBSTR | SYNTAX | SINGLE-VALUE| COLLECTIVE| NO-USER-MODIFICATION| USAGE '
446                elif cls is MatchingRuleUseInfo:
447                    pattern = '| APPLIES '
448                elif cls is LdapSyntaxInfo:
449                    pattern = ''
450                elif cls is DitContentRuleInfo:
451                    pattern = '| AUX | MUST | MAY | NOT '
452                elif cls is DitStructureRuleInfo:
453                    pattern = '| FORM | SUP '
454                elif cls is NameFormInfo:
455                    pattern = '| OC | MUST | MAY '
456                else:
457                    raise LDAPSchemaError('unknown schema definition class')
458
459                splitted = re.split('( NAME | DESC | OBSOLETE| X-| E-' + pattern + ')', object_definition[1:-1])
460                values = splitted[::2]
461                separators = splitted[1::2]
462                separators.insert(0, 'OID')
463                defs = list(zip(separators, values))
464                object_def = cls()
465                for d in defs:
466                    key = d[0].strip()
467                    value = d[1].strip()
468                    if key == 'OID':
469                        object_def.oid = value
470                    elif key == 'NAME':
471                        object_def.name = quoted_string_to_list(value)
472                    elif key == 'DESC':
473                        object_def.description = value.strip("'")
474                    elif key == 'OBSOLETE':
475                        object_def.obsolete = True
476                    elif key == 'SYNTAX':
477                        object_def.syntax = oids_string_to_list(value)
478                    elif key == 'SUP':
479                        object_def.superior = oids_string_to_list(value)
480                    elif key == 'ABSTRACT':
481                        object_def.kind = CLASS_ABSTRACT
482                    elif key == 'STRUCTURAL':
483                        object_def.kind = CLASS_STRUCTURAL
484                    elif key == 'AUXILIARY':
485                        object_def.kind = CLASS_AUXILIARY
486                    elif key == 'MUST':
487                        object_def.must_contain = oids_string_to_list(value)
488                    elif key == 'MAY':
489                        object_def.may_contain = oids_string_to_list(value)
490                    elif key == 'EQUALITY':
491                        object_def.equality = oids_string_to_list(value)
492                    elif key == 'ORDERING':
493                        object_def.ordering = oids_string_to_list(value)
494                    elif key == 'SUBSTR':
495                        object_def.substr = oids_string_to_list(value)
496                    elif key == 'SINGLE-VALUE':
497                        object_def.single_value = True
498                    elif key == 'COLLECTIVE':
499                        object_def.collective = True
500                    elif key == 'NO-USER-MODIFICATION':
501                        object_def.no_user_modification = True
502                    elif key == 'USAGE':
503                        object_def.usage = attribute_usage_to_constant(value)
504                    elif key == 'APPLIES':
505                        object_def.apply_to = oids_string_to_list(value)
506                    elif key == 'AUX':
507                        object_def.auxiliary_classes = oids_string_to_list(value)
508                    elif key == 'FORM':
509                        object_def.name_form = oids_string_to_list(value)
510                    elif key == 'OC':
511                        object_def.object_class = oids_string_to_list(value)
512                    elif key == 'NOT':
513                        object_def.not_contains = oids_string_to_list(value)
514                    elif key == 'X-':
515                        if not object_def.extensions:
516                            object_def.extensions = []
517                        object_def.extensions.append(extension_to_tuple('X-' + value))
518                    elif key == 'E-':
519                        if not object_def.experimental:
520                            object_def.experimental = []
521                        object_def.experimental.append(extension_to_tuple('E-' + value))
522                    else:
523                        if not conf_ignore_malformed_schema:
524                            raise LDAPSchemaError('malformed schema definition key:' + key + ' - use get_info=NONE in Server definition')
525                        else:
526                            return CaseInsensitiveWithAliasDict() if conf_case_insensitive_schema else dict()
527                object_def.raw_definition = object_definition
528                if hasattr(object_def, 'syntax') and object_def.syntax and len(object_def.syntax) == 1:
529                    object_def.min_length = None
530                    if object_def.syntax[0].endswith('}'):
531                        try:
532                            object_def.min_length = int(object_def.syntax[0][object_def.syntax[0].index('{') + 1:-1])
533                            object_def.syntax[0] = object_def.syntax[0][:object_def.syntax[0].index('{')]
534                        except Exception:
535                            pass
536                    else:
537                        object_def.min_length = None
538                    object_def.syntax[0] = object_def.syntax[0].strip("'")
539                    object_def.syntax = object_def.syntax[0]
540                if hasattr(object_def, 'name') and object_def.name:
541                    if conf_case_insensitive_schema:
542                        ret_dict[object_def.name[0]] = object_def
543                        ret_dict.set_alias(object_def.name[0], object_def.name[1:] + [object_def.oid], ignore_duplicates=True)
544                    else:
545                        for name in object_def.name:
546                            ret_dict[name] = object_def
547                else:
548                    ret_dict[object_def.oid] = object_def
549
550            else:
551                if not conf_ignore_malformed_schema:
552                    raise LDAPSchemaError('malformed schema definition, use get_info=NONE in Server definition')
553                else:
554                    return CaseInsensitiveWithAliasDict() if conf_case_insensitive_schema else dict()
555        return ret_dict
556
557
558class MatchingRuleInfo(BaseObjectInfo):
559    """
560    As per RFC 4512 (4.1.3)
561    """
562
563    def __init__(self,
564                 oid=None,
565                 name=None,
566                 description=None,
567                 obsolete=False,
568                 syntax=None,
569                 extensions=None,
570                 experimental=None,
571                 definition=None):
572
573        BaseObjectInfo.__init__(self,
574                                oid=oid,
575                                name=name,
576                                description=description,
577                                obsolete=obsolete,
578                                extensions=extensions,
579                                experimental=experimental,
580                                definition=definition)
581        self.syntax = syntax
582
583    def __repr__(self):
584        r = (linesep + '  Syntax: ' + list_to_string(self.syntax)) if self.syntax else ''
585        return 'Matching rule' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
586
587
588class MatchingRuleUseInfo(BaseObjectInfo):
589    """
590    As per RFC 4512 (4.1.4)
591    """
592
593    def __init__(self,
594                 oid=None,
595                 name=None,
596                 description=None,
597                 obsolete=False,
598                 apply_to=None,
599                 extensions=None,
600                 experimental=None,
601                 definition=None):
602        BaseObjectInfo.__init__(self,
603                                oid=oid,
604                                name=name,
605                                description=description,
606                                obsolete=obsolete,
607                                extensions=extensions,
608                                experimental=experimental,
609                                definition=definition)
610        self.apply_to = apply_to
611
612    def __repr__(self):
613        r = (linesep + '  Apply to: ' + list_to_string(self.apply_to)) if self.apply_to else ''
614        return 'Matching rule use' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
615
616
617class ObjectClassInfo(BaseObjectInfo):
618    """
619    As per RFC 4512 (4.1.1)
620    """
621
622    def __init__(self,
623                 oid=None,
624                 name=None,
625                 description=None,
626                 obsolete=False,
627                 superior=None,
628                 kind=None,
629                 must_contain=None,
630                 may_contain=None,
631                 extensions=None,
632                 experimental=None,
633                 definition=None):
634
635        BaseObjectInfo.__init__(self,
636                                oid=oid,
637                                name=name,
638                                description=description,
639                                obsolete=obsolete,
640                                extensions=extensions,
641                                experimental=experimental,
642                                definition=definition)
643        self.superior = superior
644        self.kind = kind
645        self.must_contain = must_contain or []
646        self.may_contain = may_contain or []
647
648    def __repr__(self):
649        r = ''
650        r += (linesep + '  Type: ' + constant_to_class_kind(self.kind)) if self.kind else ''
651        r += (linesep + '  Superior: ' + list_to_string(self.superior)) if self.superior else ''
652        r += (linesep + '  Must contain attributes: ' + list_to_string(self.must_contain)) if self.must_contain else ''
653        r += (linesep + '  May contain attributes: ' + list_to_string(self.may_contain)) if self.may_contain else ''
654        return 'Object class' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
655
656
657class AttributeTypeInfo(BaseObjectInfo):
658    """
659    As per RFC 4512 (4.1.2)
660    """
661
662    def __init__(self,
663                 oid=None,
664                 name=None,
665                 description=None,
666                 obsolete=False,
667                 superior=None,
668                 equality=None,
669                 ordering=None,
670                 substring=None,
671                 syntax=None,
672                 min_length=None,
673                 single_value=False,
674                 collective=False,
675                 no_user_modification=False,
676                 usage=None,
677                 extensions=None,
678                 experimental=None,
679                 definition=None):
680
681        BaseObjectInfo.__init__(self,
682                                oid=oid,
683                                name=name,
684                                description=description,
685                                obsolete=obsolete,
686                                extensions=extensions,
687                                experimental=experimental,
688                                definition=definition)
689        self.superior = superior
690        self.equality = equality
691        self.ordering = ordering
692        self.substring = substring
693        self.syntax = syntax
694        self.min_length = min_length
695        self.single_value = single_value
696        self.collective = collective
697        self.no_user_modification = no_user_modification
698        self.usage = usage
699        self.mandatory_in = []
700        self.optional_in = []
701
702    def __repr__(self):
703        r = ''
704        r += linesep + '  Single value: ' + str(self.single_value)
705        r += linesep + '  Collective: True' if self.collective else ''
706        r += (linesep + '  Superior: ' + list_to_string(self.superior)) if self.superior else ''
707        r += linesep + '  No user modification: True' if self.no_user_modification else ''
708        r += (linesep + '  Usage: ' + constant_to_attribute_usage(self.usage)) if self.usage else ''
709        r += (linesep + '  Equality rule: ' + list_to_string(self.equality)) if self.equality else ''
710        r += (linesep + '  Ordering rule: ' + list_to_string(self.ordering)) if self.ordering else ''
711        r += (linesep + '  Substring rule: ' + list_to_string(self.substring)) if self.substring else ''
712        r += (linesep + '  Syntax: ' + (self.syntax + (' [' + str(decode_syntax(self.syntax)))) + ']') if self.syntax else ''
713        r += (linesep + '  Minimum length: ' + str(self.min_length)) if isinstance(self.min_length, int) else ''
714        r += linesep + '  Mandatory in: ' + list_to_string(self.mandatory_in) if self.mandatory_in else ''
715        r += linesep + '  Optional in: ' + list_to_string(self.optional_in) if self.optional_in else ''
716        return 'Attribute type' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
717
718
719class LdapSyntaxInfo(BaseObjectInfo):
720    """
721    As per RFC 4512 (4.1.5)
722    """
723
724    def __init__(self,
725                 oid=None,
726                 description=None,
727                 extensions=None,
728                 experimental=None,
729                 definition=None):
730
731        BaseObjectInfo.__init__(self,
732                                oid=oid,
733                                name=None,
734                                description=description,
735                                obsolete=False,
736                                extensions=extensions,
737                                experimental=experimental,
738                                definition=definition)
739
740    def __repr__(self):
741        return 'LDAP syntax' + BaseObjectInfo.__repr__(self).replace('<__desc__>', '')
742
743
744class DitContentRuleInfo(BaseObjectInfo):
745    """
746    As per RFC 4512 (4.1.6)
747    """
748
749    def __init__(self,
750                 oid=None,
751                 name=None,
752                 description=None,
753                 obsolete=False,
754                 auxiliary_classes=None,
755                 must_contain=None,
756                 may_contain=None,
757                 not_contains=None,
758                 extensions=None,
759                 experimental=None,
760                 definition=None):
761
762        BaseObjectInfo.__init__(self,
763                                oid=oid,
764                                name=name,
765                                description=description,
766                                obsolete=obsolete,
767                                extensions=extensions,
768                                experimental=experimental,
769                                definition=definition)
770
771        self.auxiliary_classes = auxiliary_classes
772        self.must_contain = must_contain
773        self.may_contain = may_contain
774        self.not_contains = not_contains
775
776    def __repr__(self):
777        r = (linesep + '  Auxiliary classes: ' + list_to_string(self.auxiliary_classes)) if self.auxiliary_classes else ''
778        r += (linesep + '  Must contain: ' + list_to_string(self.must_contain)) if self.must_contain else ''
779        r += (linesep + '  May contain: ' + list_to_string(self.may_contain)) if self.may_contain else ''
780        r += (linesep + '  Not contains: ' + list_to_string(self.not_contains)) if self.not_contains else ''
781        return 'DIT content rule' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
782
783
784class DitStructureRuleInfo(BaseObjectInfo):
785    """
786    As per RFC 4512 (4.1.7.1)
787    """
788
789    def __init__(self,
790                 oid=None,
791                 name=None,
792                 description=None,
793                 obsolete=False,
794                 name_form=None,
795                 superior=None,
796                 extensions=None,
797                 experimental=None,
798                 definition=None):
799
800        BaseObjectInfo.__init__(self,
801                                oid=oid,
802                                name=name,
803                                description=description,
804                                obsolete=obsolete,
805                                extensions=extensions,
806                                experimental=experimental,
807                                definition=definition)
808        self.superior = superior
809        self.name_form = name_form
810
811    def __repr__(self):
812        r = (linesep + '  Superior rules: ' + list_to_string(self.superior)) if self.superior else ''
813        r += (linesep + '  Name form: ' + list_to_string(self.name_form)) if self.name_form else ''
814        return 'DIT content rule' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
815
816
817class NameFormInfo(BaseObjectInfo):
818    """
819    As per RFC 4512 (4.1.7.2)
820    """
821
822    def __init__(self,
823                 oid=None,
824                 name=None,
825                 description=None,
826                 obsolete=False,
827                 object_class=None,
828                 must_contain=None,
829                 may_contain=None,
830                 extensions=None,
831                 experimental=None,
832                 definition=None):
833
834        BaseObjectInfo.__init__(self,
835                                oid=oid,
836                                name=name,
837                                description=description,
838                                obsolete=obsolete,
839                                extensions=extensions,
840                                experimental=experimental,
841                                definition=definition)
842        self.object_class = object_class
843        self.must_contain = must_contain
844        self.may_contain = may_contain
845
846    def __repr__(self):
847        r = (linesep + '  Object class: ' + list_to_string(self.object_class)) if self.object_class else ''
848        r += (linesep + '  Must contain: ' + list_to_string(self.must_contain)) if self.must_contain else ''
849        r += (linesep + '  May contain: ' + list_to_string(self.may_contain)) if self.may_contain else ''
850        return 'DIT content rule' + BaseObjectInfo.__repr__(self).replace('<__desc__>', r)
851 
codekingpro/portable-devtools · Team Ai