web2ldap  1.7.7
About: web2ldap is a full-featured web-based LDAPv3 client.
  Fossies Dox: web2ldap-1.7.7.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

viewer.py
Go to the documentation of this file.
1# -*- coding: ascii -*-
2"""
3web2ldap.app.schema.viewer - Display LDAPv3 schema
4
5web2ldap - a web-based LDAP Client,
6see https://www.web2ldap.de for details
7
8(C) 1998-2022 by Michael Stroeder <michael@stroeder.com>
9
10This software is distributed under the terms of the
11Apache License Version 2.0 (Apache-2.0)
12https://www.apache.org/licenses/LICENSE-2.0
13"""
14
15import ldap0
16
17from ldap0.schema.subentry import (
18 SCHEMA_ATTRS,
19 SCHEMA_ATTR_MAPPING,
20 SCHEMA_CLASS_MAPPING,
21)
22from ldap0.schema.models import (
23 LDAPSyntax,
24 AttributeType,
25 ObjectClass,
26 MatchingRule,
27 MatchingRuleUse,
28 DITContentRule,
29 DITStructureRule,
30 NameForm,
31 OBJECTCLASS_KIND_STR,
32)
33
34from .. import ErrorExit
35from ..gui import (
36 footer,
37 main_menu,
38 top_section,
39)
40from ..form import OIDInput
41from ..searchform import SEARCH_OPT_ATTR_EXISTS, SEARCH_OPT_IS_EQUAL
42from . import OBSOLETE_TEMPL, schema_link_text, schema_anchor
43from .syntaxes import syntax_registry
44
45SCHEMA_VIEWER_USAGE = """
46<p>Hints:</p>
47<ul>
48 <li>You can search for schema elements by OID or name.</li>
49 <li>Wildcard search with * is supported.</li>
50 <li>For browsing choose from context menu on the right</li>
51</ul>
52"""
53
54SCHEMA_ELEMENT_HEAD_TMPL = """
55%s
56<h1>%s <em>%s</em> (%s)</h1>
57Try to look it up:
58<a id="alvestrand_oid" href="%s?https://www.alvestrand.no/objectid/%s.html">[Alvestrand]</a>
59<a id="oid-info_oid" href="%s?http://www.oid-info.com/get/%s">[oid-info.com]</a>
60<dl>
61<dt>Schema element string:</dt>
62<dd><code>%s</code></dd>
63%s
64</dl>
65"""
66
67
68def schema_anchors(app, se_names, se_class):
69 link_texts = []
70 for se_nameoroid in se_names:
71 try:
72 se_obj = app.schema.get_obj(se_class, se_nameoroid, default=None, raise_keyerror=True)
73 except KeyError:
74 link_texts.append((se_nameoroid, se_nameoroid))
75 continue
76 ltxt = schema_link_text(se_obj)
77 try:
78 schema_id = se_obj.oid
79 except AttributeError:
80 schema_id = se_obj.ruleid
81 anchor = app.anchor(
82 'oid', ltxt,
83 [
84 ('dn', app.dn),
85 ('oid', schema_id),
86 ('oid_class', SCHEMA_ATTR_MAPPING[se_class]),
87 ],
88 )
89 link_texts.append((ltxt, anchor))
90 link_texts.sort(key=lambda x: x[0].lower())
91 return [i[1] for i in link_texts]
92
93
94def schema_tree_html(app, schema, se_class, se_tree, se_oid, level):
95 """
96 returns HTML for displaying a schema descriptions inheritance tree
97 """
98 app.outf.write('<dl>')
99 se_obj = schema.get_obj(se_class, se_oid)
100 if se_obj is not None:
101 display_id = (se_obj.names or (se_oid,))[0]
102 app.outf.write(schema_anchor(app, display_id, se_class, name_template='<dt>{anchor}</dt>'))
103 if se_tree[se_oid]:
104 app.outf.write('<dd>')
105 for sub_se_oid in se_tree[se_oid]:
106 schema_tree_html(app, schema, se_class, se_tree, sub_se_oid, level+1)
107 app.outf.write('</dd>')
108 else:
109 app.outf.write('<dd></dd>')
110 app.outf.write('</dl>')
111 # end of schema_tree_html()
112
113
115 """Build context menu with schema-related items"""
116 context_menu_list = []
117 sub_schema_dn = None
118 try:
119 sub_schema_dn = app.ls.l.search_subschemasubentry_s(app.dn)
120 except ldap0.LDAPError:
121 pass
122 else:
123 if sub_schema_dn is not None:
124 form_param_list = [
125 ('dn', sub_schema_dn),
126 ('filterstr', '(objectClass=subschema)'),
127 ]
128 for schema_attr in SCHEMA_ATTRS+['objectClass', 'cn']:
129 form_param_list.append(('read_attr', schema_attr))
130 context_menu_list.append(
131 app.anchor(
132 'read', 'Subschema Subentry',
133 form_param_list,
134 title='Directly read the subschema subentry'),
135 )
136 if app.schema:
137 se_class_attrs = [
138 SCHEMA_ATTR_MAPPING[se_class]
139 for se_class in app.schema.sed.keys()
140 if app.schema.sed[se_class]
141 ]
142 se_class_attrs.sort(key=str.lower)
143 for se_class_attr in se_class_attrs:
144 context_menu_list.append(
145 app.anchor(
146 'oid', se_class_attr,
147 [('dn', app.dn), ('oid_class', se_class_attr)],
148 title='Browse all %s' % (se_class_attr,),
149 )
150 )
151 return context_menu_list
152
153
155 type_desc = 'Abstract Schema Element'
156 detail_attrs = ()
157
158 def __init__(self, app, se_obj):
159 self._app = app
160 self._schema = app.schema
161 self._se = se_obj
162 try:
163 schema_id = self._se.oid
164 except AttributeError:
165 schema_id = self._se.ruleid
166 self._sei = app.schema.get_inheritedobj(self._se.__class__, schema_id, [])
167
168 def disp_details(self):
169 for text, class_attr, se_class in self.detail_attrs:
170 class_attr_value = getattr(self._sei, class_attr, None)
171 if class_attr_value is None:
172 continue
173 if isinstance(class_attr_value, (tuple, list)):
174 class_attr_value_list = list(class_attr_value)
175 class_attr_value_list.sort(key=str.lower)
176 else:
177 class_attr_value_list = [class_attr_value]
178 if se_class is None:
179 value_output = ', '.join([
180 self._app.form.s2d(v, sp_entity=' ', lf_entity='<br>')
181 for v in class_attr_value_list
182 ])
183 else:
184 value_output = ', '.join(
185 schema_anchors(self._app, class_attr_value_list, se_class)
186 )
187 self._app.outf.write('<dt>%s</dt>\n<dd>\n%s\n</dd>\n' % (text, value_output))
188 # end of disp_details()
189
190 def display(self):
191 ms_ad_schema_link = ''
192 if 'schemaNamingContext' in self._app.ls.root_dse:
193 try:
194 result = self._app.ls.l.search_s(
195 self._app.ls.root_dse['schemaNamingContext'][0].decode(self._app.ls.charset),
196 ldap0.SCOPE_SUBTREE,
197 (
198 '(|'
199 '(&(objectClass=attributeSchema)(attributeID=%s))'
200 '(&(objectClass=classSchema)(governsID=%s))'
201 ')'
202 ) % (
203 self._se.oid,
204 self._se.oid,
205 ),
206 attrlist=['cn']
207 )
208 except ldap0.LDAPError:
209 pass
210 else:
211 if result:
212 ad_schema_dn, ad_schema_entry = result[0].dn_s, result[0].entry_s
213 ms_ad_schema_link = (
214 '<dt>Schema Definition Entry (MS AD)</dt>\n'
215 '<dd>\n%s\n</dd>\n'
216 ) % (
217 self._app.anchor(
218 'read', ad_schema_entry['cn'][0],
219 [('dn', ad_schema_dn)],
220 )
221 )
222 obsolete = getattr(self._se, 'obsolete', 0)
224 self._app,
225 '%s %s (%s)' % (
226 self.type_desc,
227 ', '.join(
228 getattr(self._se, 'names', (()))
229 ),
230 self._se.oid
231 ),
232 main_menu(self._app),
233 context_menu_list=schema_context_menu(self._app)
234 )
235 self._app.outf.write(
236 SCHEMA_ELEMENT_HEAD_TMPL % (
237 oid_input_form(self._app, ''),
238 self.type_desc,
239 OBSOLETE_TEMPL[obsolete] % (
240 ', '.join(getattr(self._se, 'names', (()))),
241 ),
242 self._se.oid,
243 self._app.form.action_url('urlredirect', self._app.sid), self._se.oid,
244 self._app.form.action_url('urlredirect', self._app.sid), self._se.oid,
245 self._app.form.s2d(str(self._se)),
246 ms_ad_schema_link,
247 )
248 )
249 self.disp_details()
250 footer(self._app)
251
252
254 type_desc = 'Object class'
255 detail_attrs = (
256 ('Description', 'desc', None),
257 ('Derived from', 'sup', ObjectClass),
258 )
259
260 def __init__(self, app, se):
261 DisplaySchemaElement.__init__(self, app, se)
262 self._sei_sei = app.schema.get_inheritedobj(self._se.__class__, self._se.oid, ['kind'])
263
264 def disp_details(self):
265 DisplaySchemaElement.disp_details(self)
266 must, may = self._schema.attribute_types([self._se.oid], raise_keyerror=False)
267 # Display all required and allowed attributes
268 self._app.outf.write('<dt>Kind of object class:</dt><dd>\n%s&nbsp;</dd>\n' % (
269 OBJECTCLASS_KIND_STR[self._sei_sei.kind],
270 ))
271 # Display all required and allowed attributes
272 self._app.outf.write('<dt>All required attributes:</dt><dd>\n%s&nbsp;</dd>\n' % (
273 ', '.join(schema_anchors(self._app, must.keys(), AttributeType)),
274 ))
275 self._app.outf.write('<dt>All allowed attributes:</dt><dd>\n%s&nbsp;</dd>\n' % (
276 ', '.join(schema_anchors(self._app, may.keys(), AttributeType)),
277 ))
278 # Display relationship to DIT content rule(s)
279 # normally only in case of a STRUCTURAL object class)
280 content_rule = self._schema.get_obj(DITContentRule, self._se.oid)
281 if content_rule:
282 self._app.outf.write(
283 '<dt>Governed by DIT content rule:</dt><dd>\n%s&nbsp;</dd>\n' % (
284 schema_anchor(self._app, content_rule.oid, DITContentRule),
285 )
286 )
287 self._app.outf.write(
288 '<dt>Applicable auxiliary object classes:</dt><dd>\n%s&nbsp;</dd>\n' % (
289 ', '.join(schema_anchors(self._app, content_rule.aux, ObjectClass)),
290 )
291 )
292 # normally only in case of a AUXILIARY object class
293 dcr_list = []
294 structural_oc_list = []
295 for _, content_rule in self._schema.sed[DITContentRule].items():
296 for aux_class_name in content_rule.aux:
297 aux_class_oid = self._schema.get_oid(ObjectClass, aux_class_name)
298 if aux_class_oid == self._se.oid:
299 dcr_list.append(content_rule.oid)
300 structural_oc_list.append(content_rule.oid)
301 if dcr_list:
302 self._app.outf.write(
303 '<dt>Referring DIT content rules:</dt><dd>\n%s&nbsp;</dd>\n' % (
304 ', '.join(schema_anchors(self._app, dcr_list, DITContentRule)),
305 )
306 )
307 if structural_oc_list:
308 self._app.outf.write(
309 '<dt>Allowed with structural object classes:</dt><dd>\n%s&nbsp;</dd>\n' % (
310 ', '.join(schema_anchors(self._app, structural_oc_list, ObjectClass)),
311 )
312 )
313 # Display name forms which regulates naming for this object class
314 oc_ref_list = []
315 for nf_oid, name_form_se in self._schema.sed[NameForm].items():
316 name_form_oc = name_form_se.oc.lower()
317 se_names = {o.lower() for o in self._sei_sei.names}
318 if name_form_se.oc == self._sei_sei.oid or name_form_oc in se_names:
319 oc_ref_list.append(nf_oid)
320 if oc_ref_list:
321 self._app.outf.write(
322 '<dt>Applicable name forms:</dt>\n<dd>\n%s\n</dd>\n' % (
323 ', '.join(schema_anchors(self._app, oc_ref_list, NameForm)),
324 )
325 )
326 # Display tree of derived object classes
327 self._app.outf.write('<dt>Object class tree:</dt>\n')
328 self._app.outf.write('<dd>\n')
329 try:
330 oc_tree = self._schema.tree(ObjectClass)
331 except KeyError as err:
332 self._app.outf.write(
333 '<strong>Missing schema elements referenced:<pre>%s</pre></strong>\n' % (
334 self._app.form.s2d(err),
335 )
336 )
337 else:
338 if self._se.oid in oc_tree and oc_tree[self._se.oid]:
339 schema_tree_html(self._app, self._schema, ObjectClass, oc_tree, self._se.oid, 0)
340 self._app.outf.write('&nbsp;</dd>\n')
341 # Display a link for searching entries by object class
342 self._app.outf.write(
343 '<dt>Search entries</dt>\n<dd>\n%s\n</dd>\n' % (
344 self._app.anchor(
345 'searchform',
346 '(objectClass=%s)' % (
347 self._app.form.s2d((self._se.names or [self._se.oid])[0]),
348 ),
349 [
350 ('dn', self._app.dn),
351 ('searchform_mode', 'adv'),
352 ('search_attr', 'objectClass'),
353 ('search_option', SEARCH_OPT_IS_EQUAL),
354 ('search_string', (self._se.names or [self._se.oid])[0]),
355 ],
356 title='Search entries by object class',
357 ),
358 )
359 )
360 # end of disp_details()
361
362
364 type_desc = 'Attribute type'
365 detail_attrs = (
366 ('Description', 'desc', None),
367 ('Syntax', 'syntax', LDAPSyntax),
368 ('Derived from', 'sup', AttributeType),
369 ('Equality matching rule', 'equality', MatchingRule),
370 ('Sub-string matching rule', 'substr', MatchingRule),
371 ('Ordering matching rule', 'ordering', MatchingRule),
372 )
373
374 def __init__(self, app, se):
375 DisplaySchemaElement.__init__(self, app, se)
376 try:
377 self._sei_sei = app.schema.get_inheritedobj(
378 self._se.__class__, self._se.oid,
379 ('syntax', 'equality', 'substr', 'ordering'),
380 )
381 except KeyError:
382 # If the schema element referenced by SUP is not present
383 self._sei_sei = app.schema.get_obj(self._se.__class__, self._se.oid)
384
385 def disp_details(self):
386
387 DisplaySchemaElement.disp_details(self)
388
389 at_oid = self._se.oid
390 syntax_oid = self._sei_sei.syntax
391
392 self._app.outf.write('<dt>Usage:</dt>\n<dd>\n%s\n</dd>\n' % (
393 {
394 0: 'userApplications',
395 1: 'directoryOperation',
396 2: 'distributedOperation',
397 3: 'dSAOperation',
398 }[self._se.usage],
399 ))
400
401 if syntax_oid is not None:
402
403 # Display applicable matching rules
404 #---------------------------------------------------------------
405 mr_use_se = self._schema.get_obj(MatchingRuleUse, syntax_oid)
406 applies_dict = {}
407 for mr_oid, mr_use_se in self._schema.sed[MatchingRuleUse].items():
408 applies_dict[mr_oid] = {}
409 mr_use_se = self._schema.get_obj(MatchingRuleUse, mr_oid)
410 for at_nameoroid in mr_use_se.applies:
411 applies_dict[mr_oid][self._schema.get_oid(AttributeType, at_nameoroid)] = None
412 # Display list of attribute types for which this matching rule is applicable
413 mr_applicable_for = [
414 mr_oid
415 for mr_oid in self._schema.sed[MatchingRule].keys()
416 if mr_oid in applies_dict and at_oid in applies_dict[mr_oid]
417 ]
418 if mr_applicable_for:
419 self._app.outf.write('<dt>Applicable matching rules:</dt>\n<dd>\n%s\n</dd>\n' % (
420 ', '.join(
421 schema_anchors(self._app, mr_applicable_for, MatchingRule)
422 ),
423 ))
424
425 # Display DIT content rules which reference attributes of this type
426 #-------------------------------------------------------------------
427 attr_type_ref_list = []
428 for oc_oid, object_class_se in self._schema.sed[ObjectClass].items():
429 object_class_se = self._schema.get_obj(ObjectClass, oc_oid)
430 for dcr_at in object_class_se.must+object_class_se.may:
431 if dcr_at == at_oid or dcr_at in self._sei_sei.names:
432 attr_type_ref_list.append(oc_oid)
433 if attr_type_ref_list:
434 self._app.outf.write(
435 '<dt>Directly referencing object classes:</dt>\n<dd>\n%s\n</dd>\n' % (
436 ', '.join(schema_anchors(self._app, attr_type_ref_list, ObjectClass)),
437 )
438 )
439
440 # Display object classes which may contain attributes of this type
441 #-------------------------------------------------------------------
442 all_object_classes = self._schema.sed[ObjectClass].keys()
443 attr_type_ref_list = []
444 for oc_oid in all_object_classes:
445 must, may = self._schema.attribute_types([oc_oid], raise_keyerror=False)
446 if at_oid in must or at_oid in may:
447 attr_type_ref_list.append(oc_oid)
448 if attr_type_ref_list:
449 self._app.outf.write(
450 '<dt>Usable in these object classes:</dt>\n<dd>\n%s\n</dd>\n' % (
451 ', '.join(schema_anchors(self._app, attr_type_ref_list, ObjectClass)),
452 )
453 )
454
455 # Display DIT content rules which reference attributes of this type
456 #-------------------------------------------------------------------
457 attr_type_ref_list = []
458 for dcr_oid, dit_content_rule_se in self._schema.sed[DITContentRule].items():
459 dit_content_rule_se = self._schema.get_obj(DITContentRule, dcr_oid)
460 for dcr_at in dit_content_rule_se.must+dit_content_rule_se.may+dit_content_rule_se.nots:
461 if dcr_at == at_oid or dcr_at in self._sei_sei.names:
462 attr_type_ref_list.append(dcr_oid)
463 if attr_type_ref_list:
464 self._app.outf.write('<dt>Referencing DIT content rules:</dt>\n<dd>\n%s\n</dd>\n' % (
465 ', '.join(schema_anchors(self._app, attr_type_ref_list, DITContentRule)),
466 ))
467
468 # Display name forms which uses this attribute type for naming an entry
469 #-----------------------------------------------------------------------
470 attr_type_ref_list = []
471 for nf_oid, name_form_se in self._schema.sed[NameForm].items():
472 name_form_se = self._schema.get_obj(NameForm, nf_oid)
473 for nf_at in name_form_se.must+name_form_se.may:
474 if nf_at == at_oid or nf_at in self._sei_sei.names:
475 attr_type_ref_list.append(nf_oid)
476 if attr_type_ref_list:
477 self._app.outf.write('<dt>Referencing name forms:</dt>\n<dd>\n%s\n</dd>\n' % (
478 ', '.join(schema_anchors(self._app, attr_type_ref_list, NameForm)),
479 ))
480
481 #########################################
482 # Output attribute type inheritance tree
483 #########################################
484 self._app.outf.write('<dt>Attribute type tree:</dt>\n<dd>\n')
485 # Display tree of derived attribute types
486 try:
487 at_tree = self._schema.tree(AttributeType)
488 except KeyError as err:
489 self._app.outf.write(
490 '<strong>Missing schema elements referenced:<pre>%s</pre></strong>\n' % (
491 self._app.form.s2d(err),
492 )
493 )
494 else:
495 if at_oid in at_tree and at_tree[at_oid]:
496 schema_tree_html(self._app, self._schema, AttributeType, at_tree, at_oid, 0)
497 # Display a link for searching entries by attribute presence
498 self._app.outf.write(
499 '</dd>\n<dt>Search entries</dt>\n<dd>\n%s\n</dd>\n' % (
500 self._app.anchor(
501 'searchform',
502 '(%s=*)' % (
503 self._app.form.s2d((self._se.names or [self._se.oid])[0]),
504 ),
505 [
506 ('dn', self._app.dn),
507 ('searchform_mode', 'adv'),
508 ('search_attr', (self._se.names or [self._se.oid])[0]),
509 ('search_option', SEARCH_OPT_ATTR_EXISTS),
510 ('search_string', ''),
511 ],
512 title='Search entries by attribute presence',
513 ),
514 )
515 )
516
517 #########################################
518 # Output registered plugin class name
519 #########################################
520 self._app.outf.write("""
521 <dt>Associated plugin class(es):</dt>
522 <dd>
523 <table>
524 <tr><th>Structural<br>object class</th><th>Plugin class</th>""")
525 for structural_oc in (syntax_registry.at2syntax[at_oid].keys() or [None]):
526 syntax_class = syntax_registry.get_syntax(
527 self._schema,
528 at_oid,
529 structural_oc,
530 )
531 if structural_oc:
532 oc_text = schema_anchor(self._app, structural_oc, ObjectClass)
533 else:
534 oc_text = '-any-'
535 self._app.outf.write('<tr><td>%s</td><td>%s.%s</td></th>\n' % (
536 oc_text,
537 self._app.form.s2d(syntax_class.__module__),
538 self._app.form.s2d(syntax_class.__name__),
539 ))
540 self._app.outf.write('</table>\n</dd>\n')
541 # end of disp_details()
542
543
545 type_desc = 'LDAP Syntax'
546 detail_attrs = (
547 ('Description', 'desc', None),
548 )
549
550 def disp_details(self):
551 DisplaySchemaElement.disp_details(self)
552 # Display list of attribute types which directly reference this syntax
553 syntax_using_at_list = [
554 at_oid
555 for at_oid in self._schema.sed[AttributeType].keys()
556 if self._schema.get_syntax(at_oid) == self._se.oid
557 ]
558 if syntax_using_at_list:
559 self._app.outf.write('<dt>Referencing attribute types:</dt>\n<dd>\n%s\n</dd>\n' % (
560 ', '.join(schema_anchors(self._app, syntax_using_at_list, AttributeType))
561 ))
562 syntax_ref_mr_list = self._schema.listall(MatchingRule, [('syntax', self._se.oid)])
563 if syntax_ref_mr_list:
564 self._app.outf.write('<dt>Referencing matching rules:</dt>\n<dd>\n%s\n</dd>\n' % (
565 ', '.join(schema_anchors(self._app, syntax_ref_mr_list, MatchingRule))
566 ))
567 try:
568 x_subst = self._se.x_subst
569 except AttributeError:
570 pass
571 else:
572 if x_subst:
573 self._app.outf.write('<dt>Substituted by:</dt>\n<dd>\n%s\n</dd>\n' % (
574 schema_anchor(self._app, x_subst, LDAPSyntax)
575 ))
576 #########################################
577 # Output registered plugin class name
578 #########################################
579 syntax_class = syntax_registry.oid2syntax.get(self._se.oid, LDAPSyntax)
580 self._app.outf.write('<dt>Associated syntax class</dt>\n<dd>\n%s\n</dd>\n' % (
581 '.'.join((syntax_class.__module__, syntax_class.__name__))
582 ))
583 # end of disp_details()
584
585
587 type_desc = 'Matching Rule'
588 detail_attrs = (
589 ('Description', 'desc', None),
590 ('LDAP syntax', 'syntax', LDAPSyntax),
591 )
592
593 def disp_details(self):
594 DisplaySchemaElement.disp_details(self)
595 mr_use_se = self._schema.get_obj(MatchingRuleUse, self._se.oid)
596 if mr_use_se:
597 applies_dict = {}
598 for at_nameoroid in mr_use_se.applies:
599 applies_dict[self._schema.get_oid(AttributeType, at_nameoroid)] = None
600 # Display list of attribute types for which this matching rule is applicable
601 mr_applicable_for = [
602 at_oid
603 for at_oid in self._schema.sed[AttributeType].keys()
604 if at_oid in applies_dict
605 ]
606 if mr_applicable_for:
607 self._app.outf.write(
608 (
609 '<dt>Applicable for attribute types per matching rule use:</dt>\n'
610 '<dd>\n%s\n</dd>\n'
611 ) % (
612 ', '.join(schema_anchors(self._app, mr_applicable_for, AttributeType)),
613 )
614 )
615 mr_used_by = []
616 mr_names = set(self._se.names)
617 for at_oid in self._schema.sed[AttributeType]:
618 try:
619 at_se = self._schema.get_inheritedobj(
620 AttributeType,
621 at_oid,
622 ('equality', 'substr', 'ordering'),
623 )
624 except KeyError:
625 continue
626 if at_se is None:
627 continue
628 at_mr_set = {at_se.equality, at_se.substr, at_se.ordering}
629 if (
630 at_se.equality in mr_names or
631 at_se.substr in mr_names or
632 at_se.ordering in mr_names or
633 self._se.oid in at_mr_set
634 ):
635 mr_used_by.append(at_se.oid)
636 if mr_used_by:
637 self._app.outf.write('<dt>Referencing attribute types:</dt>\n<dd>\n%s\n</dd>\n' % (
638 ', '.join(schema_anchors(self._app, mr_used_by, AttributeType))
639 ))
640 # end of disp_details()
641
642
644 type_desc = 'Matching Rule Use'
645 detail_attrs = (
646 ('Names', 'names', None),
647 ('Matching Rule', 'oid', MatchingRule),
648 ('Applies to', 'applies', AttributeType),
649 )
650
651
653 type_desc = 'DIT content rule'
654 detail_attrs = (
655 ('Names', 'names', None),
656 ('Governs structural object class', 'oid', ObjectClass),
657 ('Auxiliary classes', 'aux', ObjectClass),
658 ('Must have', 'must', AttributeType),
659 ('May have', 'may', AttributeType),
660 ('Must not have', 'nots', AttributeType),
661 )
662
663
665 type_desc = 'DIT structure rule'
666 detail_attrs = (
667 ('Description', 'desc', None),
668 ('Associated name form', 'form', NameForm),
669 ('Superior structure rules', 'sup', DITStructureRule),
670 )
671
672 def display(self):
674 self._app,
675 '%s %s (%s)' % (
677 ', '.join(
678 getattr(self._se, 'names', (()))
679 ),
680 self._se.ruleid
681 ),
682 main_menu(self._app),
683 context_menu_list=schema_context_menu(self._app)
684 )
685 self._app.outf.write(
686 """
687 %s
688 <h1>%s <em>%s</em> (%s)</h1>
689 <dl>
690 <dt>Schema element string:</dt>
691 <dd><code>%s</code></dd>
692 </dl>
693 """ % (
694 oid_input_form(self._app, ''),
696 ", ".join(
697 getattr(self._se, 'names', (()))
698 ),
699 self._se.ruleid,
700 self._app.form.s2d(str(self._se)),
701 )
702 )
704 footer(self._app)
705
706 def disp_details(self):
707 """
708 Display subordinate DIT structure rule(s)
709 """
710 DisplaySchemaElement.disp_details(self)
711 ditsr_rules_ref_list = []
712 for ditsr_id, ditsr_se in self._schema.sed[DITStructureRule].items():
713 if self._sei.ruleid in ditsr_se.sup:
714 ditsr_rules_ref_list.append(ditsr_id)
715 if ditsr_rules_ref_list:
716 self._app.outf.write('<dt>Subordinate DIT structure rules:</dt>\n<dd>\n%s\n</dd>\n' % (
717 ', '.join(schema_anchors(self._app, ditsr_rules_ref_list, DITStructureRule))
718 ))
719 # end of disp_details()
720
721
723 type_desc = 'Name form'
724 detail_attrs = (
725 ('Description', 'desc', None),
726 ('Structural object class this rule applies to', 'oc', ObjectClass),
727 ('Mandantory naming attributes', 'must', AttributeType),
728 ('Allowed naming attributes', 'may', AttributeType),
729 )
730
731 def disp_details(self):
732 """
733 Display referencing DIT structure rule(s)
734 """
735 DisplaySchemaElement.disp_details(self)
736 ditsr_rules_ref_list = []
737 for ditsr_id, ditsr_se in self._schema.sed[DITStructureRule].items():
738 if ditsr_se.form == self._sei.oid or ditsr_se.form in self._sei.names:
739 ditsr_rules_ref_list.append(ditsr_id)
740 if ditsr_rules_ref_list:
741 self._app.outf.write('<dt>Referencing DIT structure rule:</dt>\n<dd>\n%s\n</dd>\n' % (
742 ', '.join(schema_anchors(self._app, ditsr_rules_ref_list, DITStructureRule))
743 ))
744 # end of disp_details()
745
746
747SCHEMA_VIEWER_CLASS = {
748 ObjectClass: DisplayObjectClass,
749 AttributeType: DisplayAttributeType,
750 LDAPSyntax: DisplayLDAPSyntax,
751 MatchingRule: DisplayMatchingRule,
752 MatchingRuleUse: DisplayMatchingRuleUse,
753 DITContentRule: DisplayDITContentRule,
754 DITStructureRule: DisplayDITStructureRule,
755 NameForm: DisplayNameForm,
756}
757
758
759def oid_input_form(app, oid=None):
760 oid_input_field_html = OIDInput(
761 'oid',
762 'OID or descriptive name of schema element',
763 default=oid
764 ).input_html(oid)
765 oid_class_select_html = app.form.field['oid_class'].input_html('')
766 return app.form_html(
767 'oid', 'Search', 'GET',
768 [('dn', app.dn)],
769 extrastr='\n'.join((oid_input_field_html, oid_class_select_html)),
770 )
771
772
773def display_schema_elements(app, se_classes, se_list):
774 se_list = se_list or []
775 se_classes = tuple(filter(None, se_classes or []) or SCHEMA_CLASS_MAPPING.values())
776
778 app,
779 'Schema elements',
780 main_menu(app),
781 context_menu_list=schema_context_menu(app)
782 )
783
784 if app.schema is None:
785 raise ErrorExit('No sub schema available!')
786
787 oid_dict = {}
788 if se_list:
789 for schema_class in se_classes:
790 oid_dict[schema_class] = []
791 for se_obj in se_list:
792 try:
793 se_id = se_obj.oid
794 except AttributeError:
795 se_id = se_obj.ruleid
796 try:
797 oid_dict[se_obj.__class__].append(se_id)
798 except KeyError:
799 oid_dict[se_obj.__class__] = [se_id]
800 else:
801 for schema_class in se_classes:
802 oid_dict[schema_class] = app.schema.sed[schema_class].keys()
803 app.outf.write(oid_input_form(app, ''))
804
805 if oid_dict:
806 for schema_class, schema_elements in oid_dict.items():
807 if not schema_elements:
808 continue
809 app.outf.write('<h2>%s</h2>\n<p>found %d</p>\n%s\n' % (
810 SCHEMA_VIEWER_CLASS[schema_class].type_desc,
811 len(schema_elements),
812 ',\n '.join(schema_anchors(app, schema_elements, schema_class)),
813 ))
814 else:
815 app.outf.write(SCHEMA_VIEWER_USAGE)
816 footer(app)
817 # end of display_schema_elements()
818
819
821
822 def contains_oid(val, oid):
823 return val.__contains__(oid)
824
825 def startswith_oid(val, oid):
826 return val.startswith(oid)
827
828 def endswith_oid(val, oid):
829 return val.endswith(oid)
830
831 # Get input parameter from form input
832 oid = app.form.getInputValue('oid', [None])[0]
833 se_classes = [
834 SCHEMA_CLASS_MAPPING[se_name]
835 for se_name in app.form.getInputValue('oid_class', [])
836 if se_name
837 ]
838
839 if not oid:
840 # Display entry page of schema browser
841 display_schema_elements(app, se_classes, None)
842 return
843
844 # Sanitize oid
845 oid = oid.strip()
846 if oid.lower().endswith(';binary'):
847 oid = oid[:-7]
848
849 # Determine the matching method, e.g. for wildcard search
850 if oid.startswith('*') and oid.endswith('*'):
851 oid_mv = oid[1:-1].lower()
852 cmp_method = contains_oid
853 elif oid.startswith('*'):
854 oid_mv = oid[1:].lower()
855 cmp_method = endswith_oid
856 elif oid.endswith('*'):
857 oid_mv = oid[:-1].lower()
858 cmp_method = startswith_oid
859 else:
860 cmp_method = None
861
862 if len(se_classes) == 1 and cmp_method is None:
863 # Display a single schema element referenced by OID and class
864 se_list = []
865 se_obj = app.schema.get_obj(se_classes[0], oid, None)
866 if se_obj is not None:
867 se_list.append(se_obj)
868 else:
869 # Search schema element by OID
870 se_list = []
871 if cmp_method is None:
872 # No wildcard search => just try to look up directly via name or OID
873 for schema_element_type in se_classes or SCHEMA_VIEWER_CLASS.keys():
874 try:
875 se_obj = app.schema.get_obj(schema_element_type, oid, None, raise_keyerror=True)
876 except KeyError:
877 pass
878 else:
879 se_list.append(se_obj)
880 else:
881 # Do a wildcard search
882 for schema_element_type in se_classes or SCHEMA_VIEWER_CLASS.keys():
883 for se_obj in app.schema.sed[schema_element_type].values():
884 try:
885 se_id = se_obj.oid
886 except AttributeError:
887 se_id = se_obj.ruleid
888 if cmp_method(se_id.lower(), oid_mv):
889 # OID matched
890 se_list.append(se_obj)
891 else:
892 # Look whether a value of NAMEs match
893 try:
894 se_names = se_obj.names
895 except AttributeError:
896 continue
897 for se_name in se_names or []:
898 if cmp_method(se_name.lower(), oid_mv):
899 se_list.append(se_obj)
900 break
901
902 if not se_list:
903 # Display error message with input form
904 app.simple_message(
905 title='',
906 message=(
907 '<h1>Schema elements</h1><p class="ErrorMessage">'
908 'Name or OID not found in schema!</p><p>%s</p>'
909 ) % (
910 oid_input_form(app, oid),
911 ),
912 main_div_id='Message',
913 main_menu_list=main_menu(app),
914 context_menu_list=schema_context_menu(app)
915 )
916 return
917 if len(se_list) > 1:
918 # Display a list of schema elements to choose from
919 display_schema_elements(app, None, se_list)
920 return
921
922 # Directly display a single schema element
923 se_obj = se_list[0]
924 if se_obj.__class__ not in SCHEMA_VIEWER_CLASS:
925 raise ErrorExit('No viewer for this type of schema element!')
926 schema_viewer = SCHEMA_VIEWER_CLASS[se_obj.__class__](app, se_obj)
927 schema_viewer.display()
def top_section(app, title, main_menu_list, context_menu_list=None, main_div_id='Message')
Definition: gui.py:352
def main_menu(app)
Definition: gui.py:234
def footer(app)
Definition: gui.py:477
def schema_anchors(app, se_names, se_class)
Definition: viewer.py:68
def schema_tree_html(app, schema, se_class, se_tree, se_oid, level)
Definition: viewer.py:94
def oid_input_form(app, oid=None)
Definition: viewer.py:759
def schema_context_menu(app)
Definition: viewer.py:114
def display_schema_elements(app, se_classes, se_list)
Definition: viewer.py:773
def w2l_schema_viewer(app)
Definition: viewer.py:820
def schema_anchor(app, se_nameoroid, se_class, name_template='{name}\n{anchor}', link_text=None)
Definition: __init__.py:205
def schema_link_text(se_obj)
Definition: __init__.py:176