codekingpro/portable-devtools
114k
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 