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)  

aedir.py
Go to the documentation of this file.
1# -*- coding: ascii -*-
2"""
3web2ldap plugin classes for
4
5\xC6-DIR -- Authorized Entities Directory
6"""
7
8# Python's standard lib
9import re
10import time
11import socket
12from typing import Dict, List, Optional
13
14# from ldap0 package
15import ldap0
16import ldap0.filter
17from ldap0.filter import escape_str as escape_filter_str
18from ldap0.functions import strf_secs as ldap0_strf_secs
19from ldap0.pw import random_string
20from ldap0.controls.readentry import PreReadControl
21from ldap0.controls.deref import DereferenceControl
22from ldap0.filter import compose_filter, map_filter_parts
23from ldap0.dn import DNObj
24from ldap0.res import SearchResultEntry
25from ldap0.base import decode_list
26
27import web2ldapcnf
28
29from ...log import logger
30from ...web.forms import HiddenInput, Field
31from ..searchform import (
32 SEARCH_OPT_IS_EQUAL,
33 SEARCH_OPT_DN_SUBTREE,
34)
35from .nis import UidNumber, GidNumber, MemberUID, Shell
36from .inetorgperson import DisplayNameInetOrgPerson, CNInetOrgPerson
37from .groups import GroupEntryDN
38from .oath import OathHOTPToken
39from .opensshlpk import SshPublicKey
40from .posixautogen import HomeDirectory
41from .ppolicy import PwdPolicySubentry
42from .sudoers import SudoUserGroup
43from ..schema.syntaxes import (
44 ComposedAttribute,
45 DirectoryString,
46 DistinguishedName,
47 DNSDomain,
48 DerefDynamicDNSelectList,
49 DynamicValueSelectList,
50 IA5String,
51 Integer,
52 NotAfter,
53 NotBefore,
54 RFC822Address,
55 SelectList,
56 syntax_registry,
57)
58from .. import ErrorExit
59
60
61# OID arc for AE-DIR, see stroeder.com-oid-macros.schema
62AE_OID_PREFIX = '1.3.6.1.4.1.5427.1.389.100'
63
64# OIDs of AE-DIR's structural object classes
65AE_USER_OID = AE_OID_PREFIX+'.6.2'
66AE_GROUP_OID = AE_OID_PREFIX+'.6.1'
67AE_MAILGROUP_OID = AE_OID_PREFIX+'.6.27'
68AE_SRVGROUP_OID = AE_OID_PREFIX+'.6.13'
69AE_SUDORULE_OID = AE_OID_PREFIX+'.6.7'
70AE_HOST_OID = AE_OID_PREFIX+'.6.6.1'
71AE_SERVICE_OID = AE_OID_PREFIX+'.6.4'
72AE_ZONE_OID = AE_OID_PREFIX+'.6.20'
73AE_PERSON_OID = AE_OID_PREFIX+'.6.8'
74AE_TAG_OID = AE_OID_PREFIX+'.6.24'
75AE_POLICY_OID = AE_OID_PREFIX+'.6.26'
76AE_AUTHCTOKEN_OID = AE_OID_PREFIX+'.6.25'
77AE_DEPT_OID = AE_OID_PREFIX+'.6.29'
78AE_CONTACT_OID = AE_OID_PREFIX+'.6.5'
79AE_LOCATION_OID = AE_OID_PREFIX+'.6.35'
80AE_NWDEVICE_OID = AE_OID_PREFIX+'.6.6.2'
81
82
83syntax_registry.reg_at(
84 DNSDomain.oid, [
85 AE_OID_PREFIX+'.4.10', # aeFqdn
86 ]
87)
88
89
90def ae_validity_filter(secs=None):
91 if secs is None:
92 secs = time.time()
93 return (
94 '(&'
95 '(|(!(aeNotBefore=*))(aeNotBefore<={0}))'
96 '(|(!(aeNotAfter=*))(aeNotAfter>={0}))'
97 ')'
98 ).format(ldap0_strf_secs(secs))
99
100
102 """
103 utility mix-in class for all aeObject entries
104 """
105
106 @property
107 def ae_status(self):
108 try:
109 ae_status = int(self._entry['aeStatus'][0])
110 except (KeyError, ValueError, IndexError):
111 ae_status = None
112 return ae_status
113
114 def _zone_entry(self, attrlist=None):
115 zone_dn = 'cn={0},{1}'.format(
116 self._get_zone_name(),
117 self._app.naming_context,
118 )
119 try:
120 zone = self._app.ls.l.read_s(
121 zone_dn,
122 attrlist=attrlist,
123 filterstr='(objectClass=aeZone)',
124 )
125 except ldap0.LDAPError:
126 res = {}
127 else:
128 if zone is None:
129 res = {}
130 else:
131 res = zone.entry_s
132 return res
133
134 def _get_zone_dn(self) -> str:
135 return str(self.dn.slice(-len(DNObj.from_str(self._app.naming_context))-1, None))
136
137 def _get_zone_name(self) -> str:
138 return self.dn[-len(DNObj.from_str(self._app.naming_context))-1][0][1]
139
140
142 """
143 Plugin for attribute 'homeDirectory' in aeUser and aeService entries
144 """
145 oid: str = 'AEHomeDirectory-oid'
146 # all valid directory prefixes for attribute 'homeDirectory'
147 # but without trailing slash
148 homeDirectoryPrefixes = (
149 '/home',
150 )
151 homeDirectoryHidden = '-/-'
152
153 def _validate(self, attr_value: bytes) -> bool:
154 av_u = self._app.ls.uc_decode(attr_value)[0]
155 if av_u == self.homeDirectoryHidden:
156 return True
157 for prefix in self.homeDirectoryPrefixes:
158 if av_u.startswith(prefix):
159 uid = self._app.ls.uc_decode(self._entry.get('uid', [b''])[0])[0]
160 return av_u.endswith(uid)
161 return False
162
163 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
164 if attr_values == [self.homeDirectoryHidden]:
165 return attr_values
166 if 'uid' in self._entry:
167 uid = self._app.ls.uc_decode(self._entry['uid'][0])[0]
168 else:
169 uid = ''
170 if attr_values:
171 av_u = self._app.ls.uc_decode(attr_values[0])[0]
172 for prefix in self.homeDirectoryPrefixes:
173 if av_u.startswith(prefix):
174 break
175 else:
176 prefix = self.homeDirectoryPrefixes[0]
177 else:
178 prefix = self.homeDirectoryPrefixes[0]
179 return [self._app.ls.uc_encode('/'.join((prefix, uid)))[0]]
180
181 def input_field(self) -> Field:
182 input_field = HiddenInput(
183 self._at,
184 ': '.join([self._at, self.desc]),
185 self.max_len,
186 self.max_values,
187 None,
188 default=self.form_value()
189 )
190 input_field.charset = self._app.form.accept_charset
191 return input_field
192
193syntax_registry.reg_at(
194 AEHomeDirectory.oid, [
195 '1.3.6.1.1.1.1.3', # homeDirectory
196 ],
197 structural_oc_oids=[AE_USER_OID, AE_SERVICE_OID], # aeUser and aeService
198)
199
200
202 """
203 Plugin for attribute 'uidNumber' in aeUser and aeService entries
204 """
205 oid: str = 'AEUIDNumber-oid'
206 desc: str = 'numeric Unix-UID'
207
208 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
209 return self._entry.get('gidNumber', [b''])
210
211 def input_field(self) -> Field:
212 input_field = HiddenInput(
213 self._at,
214 ': '.join([self._at, self.desc]),
215 self.max_len, self.max_values, None,
216 default=self.form_value()
217 )
218 input_field.charset = self._app.form.accept_charset
219 return input_field
220
221syntax_registry.reg_at(
222 AEUIDNumber.oid, [
223 '1.3.6.1.1.1.1.0', # uidNumber
224 ],
225 structural_oc_oids=[
226 AE_USER_OID, # aeUser
227 AE_SERVICE_OID, # aeService
228 ],
229)
230
231
233 """
234 Plugin for attribute 'gidNumber' in aeUser, aeGroup and aeService entries
235 """
236 oid: str = 'AEGIDNumber-oid'
237 desc: str = 'numeric Unix-GID'
238 minNewValue = 30000
239 maxNewValue = 49999
240 id_pool_dn = None
241
242 def _get_id_pool_dn(self) -> str:
243 """
244 determine which ID pool entry to use
245 """
246 return self.id_pool_dn or str(self._app.naming_context)
247
248 def _get_next_gid(self) -> int:
249 """
250 consumes next ID by sending MOD_INCREMENT modify operation with
251 pre-read entry control
252 """
253 prc = PreReadControl(criticality=True, attrList=[self._at])
254 ldap_result = self._app.ls.l.modify_s(
255 self._get_id_pool_dn(),
256 [(ldap0.MOD_INCREMENT, self._app.ls.uc_encode(self._at)[0], [b'1'])],
257 req_ctrls=[prc],
258 )
259 return int(ldap_result.ctrls[0].res.entry_s[self._at][0])
260
261 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
262 if attr_values and attr_values[0]:
263 return attr_values
264 # first try to re-read gidNumber from existing entry
265 try:
266 ldap_result = self._app.ls.l.read_s(
267 self._dn,
268 attrlist=[self._at],
269 filterstr='({0}=*)'.format(self._at),
270 )
271 except (
272 ldap0.NO_SUCH_OBJECT,
273 ldap0.INSUFFICIENT_ACCESS,
274 ):
275 # search failed => ignore
276 pass
277 else:
278 if ldap_result:
279 return ldap_result.entry_as[self._at]
280 # return next ID from pool entry
281 return [str(self._get_next_gid()).encode('ascii')]
282
283 def form_value(self) -> str:
284 return Integer.form_value(self)
285
286 def input_field(self) -> Field:
287 return Integer.input_field(self)
288
289syntax_registry.reg_at(
290 AEGIDNumber.oid, [
291 '1.3.6.1.1.1.1.1', # gidNumber
292 ],
293 structural_oc_oids=[
294 AE_USER_OID, # aeUser
295 AE_GROUP_OID, # aeGroup
296 AE_SERVICE_OID, # aeService
297 ],
298)
299
300
302 """
303 Base class for attribute 'uid' mainly for sanitizing input values
304 """
305 oid: str = 'AEUid-oid'
306 sani_funcs = (
307 bytes.strip,
308 bytes.lower,
309 )
310
311
313 """
314 Class for auto-generating values for aeUser -> uid
315 """
316 oid: str = 'AEUserUid-oid'
317 desc: str = 'AE-DIR: User name'
318 max_values = 1
319 min_len: int = 4
320 max_len: int = 4
321 maxCollisionChecks: int = 15
322 UID_LETTERS = 'abcdefghijklmnopqrstuvwxyz'
323 pattern = re.compile('^[{}]+$'.format(UID_LETTERS))
324 genLen = 4
325 sani_funcs = (
326 bytes.strip,
327 bytes.lower,
328 )
329
330 def __init__(self, app, dn: str, schema, attrType: str, attr_value: bytes, entry=None):
331 IA5String.__init__(self, app, dn, schema, attrType, attr_value, entry=entry)
332
333 def _gen_uid(self):
334 uid_candidates = []
335 while len(uid_candidates) < self.maxCollisionChecks:
336 # generate new random UID candidate
337 uid_candidate = random_string(alphabet=self.UID_LETTERS, length=self.genLen)
338 # check whether UID candidate already exists
339 uid_result = self._app.ls.l.search_s(
340 str(self._app.naming_context),
341 ldap0.SCOPE_SUBTREE,
342 '(uid=%s)' % (escape_filter_str(uid_candidate)),
343 attrlist=['1.1'],
344 )
345 if not uid_result:
346 logger.info(
347 'Generated aeUser-uid after %d collisions: %r',
348 len(uid_candidates),
349 uid_candidate,
350 )
351 return uid_candidate
352 uid_candidates.append(uid_candidate)
353 logger.error(
354 'Generating aeUser-uid stopped after %d collisions. Tried candidates: %r',
355 len(uid_candidates),
356 uid_candidates,
357 )
358 raise ErrorExit(
359 'Gave up generating new unique <em>uid</em> after {0:d} attempts.'.format(
360 len(uid_candidates),
361 )
362 )
363 # end of _gen_uid()
364
365 def form_value(self) -> str:
366 fval = IA5String.form_value(self)
367 if not self._av:
368 fval = self._gen_uid()
369 return fval
370
371 def input_field(self) -> Field:
372 return HiddenInput(
373 self._at,
374 ': '.join([self._at, self.desc]),
375 self.max_len, self.max_values, None,
376 default=self.form_valueform_value()
377 )
378
379 def sanitize(self, attr_value: bytes) -> bytes:
380 return attr_value.strip().lower()
381
382syntax_registry.reg_at(
383 AEUserUid.oid, [
384 '0.9.2342.19200300.100.1.1', # uid
385 ],
386 structural_oc_oids=[
387 AE_USER_OID, # aeUser
388 ],
389)
390
391
393 """
394 Plugin for attribute 'uid' in aeService entries
395 """
396 oid: str = 'AEServiceUid-oid'
397
398syntax_registry.reg_at(
399 AEServiceUid.oid, [
400 '0.9.2342.19200300.100.1.1', # uid
401 ],
402 structural_oc_oids=[
403 AE_SERVICE_OID, # aeService
404 ],
405)
406
407
409 """
410 Plugin for attribute 'aeTicketId' in all aeObject entries
411 """
412 oid: str = 'AETicketId-oid'
413 desc: str = 'AE-DIR: Ticket no. related to last change of entry'
414 sani_funcs = (
415 bytes.upper,
416 bytes.strip,
417 )
418
419syntax_registry.reg_at(
420 AETicketId.oid, [
421 AE_OID_PREFIX+'.4.3', # aeTicketId
422 ]
423)
424
425
427 """
428 custom variant with smarter handling of search base
429 """
430 oid: str = 'AERootDynamicDNSelectList-oid'
431 input_fallback = False # no fallback to normal input field
432 suffix_attr = 'aeRoot'
433
434 def _search_root(self) -> str:
435 if self.lu_obj.dn == self.suffix_attr:
436 try:
437 ae_suffix = self._app.ls.l.read_rootdse_s(
438 attrlist=['self.suffix_attr']
439 ).entry_s[self.suffix_attr][0]
440 except (ldap0.LDAPError, KeyError):
441 pass
442 else:
443 return ae_suffix
444 return DerefDynamicDNSelectList._search_root(self)
445
446
448 """
449 Plugin for attributes holding DNs of aeZone entries
450 """
451 oid: str = 'AEZoneDN-oid'
452 desc: str = 'AE-DIR: Zone'
453 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeZone)(aeStatus=0))'
454 ref_attrs = (
455 (None, 'Same zone', None, 'aeGroup', 'Search all groups constrained to same zone'),
456 )
457
458syntax_registry.reg_at(
459 AEZoneDN.oid, [
460 AE_OID_PREFIX+'.4.36', # aeMemberZone
461 ]
462)
463
464
466 """
467 Plugin for attribute 'host' in aeHost entries
468 """
469 oid: str = 'AEHost-oid'
470 desc: str = 'AE-DIR: Host'
471 ldap_url = 'ldap:///_?host?sub?(&(objectClass=aeHost)(aeStatus=0))'
472 ref_attrs = (
473 (None, 'Same host', None, 'aeService', 'Search all services running on same host'),
474 )
475
476syntax_registry.reg_at(
477 AEHost.oid, [
478 AE_OID_PREFIX+'.4.28', # aeHost
479 ]
480)
481
482
484 """
485 Plugin for attributes holding DNs of aeNwDevice entries
486 """
487 oid: str = 'AENwDevice-oid'
488 desc: str = 'AE-DIR: network interface'
489 ldap_url = 'ldap:///..?cn?sub?(&(objectClass=aeNwDevice)(aeStatus=0))'
490 ref_attrs = (
491 (None, 'Siblings', None, 'aeNwDevice', 'Search sibling network devices'),
492 )
493
494 def _search_root(self) -> str:
495 if self._dn.startswith('host='):
496 return self._dn
497 return DerefDynamicDNSelectList._search_root(self)
498
499 def _filterstr(self):
500 orig_filter = DerefDynamicDNSelectList._filterstr(self)
501 try:
502 dev_name = self._app.ls.uc_decode(self._entry['cn'][0])[0]
503 except (KeyError, IndexError):
504 result_filter = orig_filter
505 else:
506 result_filter = '(&{0}(!(cn={1})))'.format(orig_filter, dev_name)
507 return result_filter
508
509syntax_registry.reg_at(
510 AENwDevice.oid, [
511 AE_OID_PREFIX+'.4.34', # aeNwDevice
512 ]
513)
514
515
517 """
518 Plugin for attribute 'member' in aeGroup entries
519 """
520 oid: str = 'AEGroupMember-oid'
521 desc: str = 'AE-DIR: Member of a group'
522 input_fallback = False # no fallback to normal input field
523 ldap_url = (
524 'ldap:///_?displayName?sub?'
525 '(&(|(objectClass=aeUser)(objectClass=aeService))(aeStatus=0))'
526 )
527 deref_person_attrs = ('aeDept', 'aeLocation')
528
529 def _zone_filter(self):
530 member_zones = [
531 self._app.ls.uc_decode(mezo)[0]
532 for mezo in self._entry.get('aeMemberZone', [])
533 if mezo
534 ]
535 if member_zones:
536 member_zone_filter = compose_filter(
537 '|',
538 map_filter_parts('entryDN:dnSubordinateMatch:', member_zones),
539 )
540 else:
541 member_zone_filter = ''
542 return member_zone_filter
543
545 result = {}
546 for attr_type in self.deref_person_attrs:
547 if attr_type in self._entry and list(filter(None, self._entry[attr_type])):
548 result[attr_type] = set(self._entry[attr_type])
549 return result
550
551 def _filterstr(self):
552 return '(&{0}{1})'.format(
553 DerefDynamicDNSelectList._filterstr(self),
554 self._zone_filter(),
555 )
556
557 def _extract_attr_value_dict(self, ldap_result, deref_person_attrset):
558 attr_value_dict: Dict[str, str] = SelectList.get_attr_value_dict(self)
559 for ldap_res in ldap_result:
560 if not isinstance(ldap_res, SearchResultEntry):
561 # ignore search continuations
562 continue
563 # process dn and entry
564 if ldap_res.ctrls:
565 deref_control = ldap_res.ctrls[0]
566 deref_entry = deref_control.derefRes['aePerson'][0].entry_as
567 elif deref_person_attrset:
568 # if we have constrained attributes, no deref response control
569 # means constraint not valid
570 continue
571 # check constrained values here
572 valid = True
573 for attr_type, attr_values in deref_person_attrset.items():
574 if (
575 attr_type not in deref_entry
576 or deref_entry[attr_type][0] not in attr_values
577 ):
578 valid = False
579 break
580 if valid:
581 option_value = ldap_res.dn_s
582 try:
583 option_text = ldap_res.entry_s['displayName'][0]
584 except KeyError:
585 option_text = option_value
586 try:
587 option_title = ldap_res.entry_s['description'][0]
588 except KeyError:
589 option_title = option_value
590 attr_value_dict[option_value] = (option_text, option_title)
591 return attr_value_dict
592
593 def get_attr_value_dict(self) -> Dict[str, str]:
594 deref_person_attrset = self._deref_person_attrset()
595 if not deref_person_attrset:
596 return DerefDynamicDNSelectList.get_attr_value_dict(self)
597 member_filter = self._filterstr_filterstr()
598 try:
599 # Use the existing LDAP connection as current user
600 ldap_result = self._app.ls.l.search_s(
601 self._search_root(),
602 self.lu_obj.scope or ldap0.SCOPE_SUBTREE,
603 filterstr=member_filter,
604 attrlist=self.lu_obj.attrs+['description'],
605 req_ctrls=[
606 DereferenceControl(True, {'aePerson': deref_person_attrset.keys()})
607 ],
608 cache_ttl=min(30, 5*web2ldapcnf.ldap_cache_ttl),
609 )
610 except self.ignored_errors as ldap_err:
611 logger.warning(
612 '%s.get_attr_value_dict() searching %r failed: %s',
613 self.__class__.__name__,
614 member_filter,
615 ldap_err,
616 )
617 return SelectList.get_attr_value_dict(self)
618 return self._extract_attr_value_dict(ldap_result, deref_person_attrset)
619 # get_attr_value_dict()
620
621 def _validate(self, attr_value: bytes) -> bool:
622 if 'memberURL' in self._entry and self._entry['memberURL'] != [b'']:
623 # reduce to simple DN syntax check for dynamic groups
624 return DistinguishedName._validate(self, attr_value)
625 return SelectList._validate(self, attr_value)
626
627 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
628 if self.ae_statusae_status == 2:
629 return []
630 return DerefDynamicDNSelectList.transmute(self, attr_values)
631
632syntax_registry.reg_at(
633 AEGroupMember.oid, [
634 '2.5.4.31', # member
635 ],
636 structural_oc_oids=[
637 AE_GROUP_OID, # aeGroup
638 ],
639)
640
641
643 """
644 Plugin for attribute 'member' in aeMailGroup entries
645 """
646 oid: str = 'AEMailGroupMember-oid'
647 desc: str = 'AE-DIR: Member of a mail group'
648 input_fallback = False # no fallback to normal input field
649 ldap_url = (
650 'ldap:///_?displayName?sub?'
651 '(&(|(objectClass=inetLocalMailRecipient)(objectClass=aeContact))(mail=*)(aeStatus=0))'
652 )
653
654syntax_registry.reg_at(
655 AEMailGroupMember.oid, [
656 '2.5.4.31', # member
657 ],
658 structural_oc_oids=[
659 AE_MAILGROUP_OID, # aeMailGroup
660 ],
661)
662
663
665 """
666 Plugin for attribute 'memberUid' in aeGroup entries
667 """
668 oid: str = 'AEMemberUid-oid'
669 desc: str = 'AE-DIR: username (uid) of member of a group'
670 ldap_url = None
671 show_val_button = False
672
674 return [
675 dn[4:].split(b',')[0]
676 for dn in self._entry.get('member', [])
677 ]
678
679 def _validate(self, attr_value: bytes) -> bool:
680 """
681 Because AEMemberUid.transmute() always resets all attribute values it's
682 ok to not validate values at all
683 """
684 return True
685
686 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
687 if 'member' not in self._entry:
688 return []
689 if self.ae_statusae_status == 2:
690 return []
691 return list(filter(None, self._member_uids_from_member()))
692
693 def form_value(self) -> str:
694 return ''
695
696 def input_field(self) -> Field:
697 input_field = HiddenInput(
698 self._at,
699 ': '.join([self._at, self.desc]),
700 self.max_len, self.max_values, None,
701 )
702 input_field.charset = self._app.form.accept_charset
703 input_field.set_default(self.form_valueform_value())
704 return input_field
705
706 def display(self, vidx, links) -> str:
707 return IA5String.display(self, vidx, links)
708
709syntax_registry.reg_at(
710 AEMemberUid.oid, [
711 '1.3.6.1.1.1.1.12', # memberUid
712 ],
713 structural_oc_oids=[
714 AE_GROUP_OID, # aeGroup
715 ],
716)
717
718
720 """
721 Plugin for attribute 'memberOf' in group member entries
722 """
723 oid: str = 'AEGroupDN-oid'
724 desc: str = 'AE-DIR: DN of user group entry'
725 ldap_url = 'ldap:///_??sub?(&(|(objectClass=aeGroup)(objectClass=aeMailGroup))(aeStatus=0))'
726 ref_attrs = (
727 ('memberOf', 'Members', None, 'Search all member entries of this user group'),
728 )
729
730 def display(self, vidx, links) -> str:
731 group_dn = DNObj.from_str(self.av_u)
732 group_cn = group_dn[0][0][1]
733 res = [
734 'cn=<strong>{0}</strong>,{1}'.format(
735 self._app.form.s2d(group_cn),
736 self._app.form.s2d(str(group_dn.parent())),
737 )
738 ]
739 if links:
740 res.extend(self._additional_links())
741 return web2ldapcnf.command_link_separator.join(res)
742
743syntax_registry.reg_at(
744 AEGroupDN.oid, [
745 '1.2.840.113556.1.2.102', # memberOf
746 ],
747 structural_oc_oids=[
748 AE_USER_OID, # aeUser
749 AE_SERVICE_OID, # aeService
750 AE_CONTACT_OID, # aeContact
751 ],
752)
753
754
756 """
757 Plugin for attributes holding DNs of zone admin groups
758 """
759 oid: str = 'AEZoneAdminGroupDN-oid'
760 desc: str = 'AE-DIR: DN of zone admin group entry'
761 ldap_url = (
762 'ldap:///_??sub?'
763 '(&'
764 '(objectClass=aeGroup)'
765 '(aeStatus=0)'
766 '(cn=*-zone-admins)'
767 '(!'
768 '(|'
769 '(cn:dn:=pub)'
770 '(cn:dn:=ae)'
771 ')'
772 ')'
773 ')'
774 )
775
776syntax_registry.reg_at(
777 AEZoneAdminGroupDN.oid, [
778 AE_OID_PREFIX+'.4.31', # aeZoneAdmins
779 AE_OID_PREFIX+'.4.33', # aePasswordAdmins
780 ]
781)
782
783
785 """
786 Plugin for attributes holding DNs of zone auditor groups
787 """
788 oid: str = 'AEZoneAuditorGroupDN-oid'
789 desc: str = 'AE-DIR: DN of zone auditor group entry'
790 ldap_url = (
791 'ldap:///_??sub?'
792 '(&'
793 '(objectClass=aeGroup)'
794 '(aeStatus=0)'
795 '(|'
796 '(cn=*-zone-admins)'
797 '(cn=*-zone-auditors)'
798 ')'
799 '(!'
800 '(|'
801 '(cn:dn:=pub)'
802 '(cn:dn:=ae)'
803 ')'
804 ')'
805 ')'
806 )
807
808syntax_registry.reg_at(
809 AEZoneAuditorGroupDN.oid, [
810 AE_OID_PREFIX+'.4.32', # aeZoneAuditors
811 ]
812)
813
814
816 """
817 Plugin class for attributes holding DNs of user groups
818 in aeSrvGroup entries
819 """
820 oid: str = 'AESrvGroupRightsGroupDN-oid'
821 desc: str = 'AE-DIR: DN of user group entry'
822 ldap_url = (
823 'ldap:///_??sub?'
824 '(&'
825 '(objectClass=aeGroup)'
826 '(aeStatus=0)'
827 '(!'
828 '(|'
829 '(cn:dn:=pub)'
830 '(cn=*-zone-admins)'
831 '(cn=*-zone-auditors)'
832 ')'
833 ')'
834 ')'
835 )
836
837syntax_registry.reg_at(
838 AESrvGroupRightsGroupDN.oid, [
839 AE_OID_PREFIX+'.4.4', # aeLoginGroups
840 AE_OID_PREFIX+'.4.6', # aeSetupGroups
841 AE_OID_PREFIX+'.4.7', # aeLogStoreGroups
842 AE_OID_PREFIX+'.4.37', # aeABAccessGroups
843 ]
844)
845
846
848 """
849 Plugin class for attribute 'aeDisplayNameGroups' in aeSrvGroup entries
850 """
851 oid: str = 'AEDisplayNameGroups-oid'
852 desc: str = 'AE-DIR: DN of visible user group entry'
853 ldap_url = (
854 'ldap:///_??sub?'
855 '(&'
856 '(|'
857 '(objectClass=aeGroup)'
858 '(objectClass=aeMailGroup)'
859 ')'
860 '(aeStatus=0)'
861 '(!'
862 '(|'
863 '(cn:dn:=pub)'
864 '(cn=*-zone-admins)'
865 '(cn=*-zone-auditors)'
866 ')'
867 ')'
868 ')'
869 )
870
871syntax_registry.reg_at(
872 AEDisplayNameGroups.oid, [
873 AE_OID_PREFIX+'.4.30', # aeDisplayNameGroups
874 ]
875)
876
877
879 """
880 Plugin class for attribute 'aeVisibleGroups' in aeSrvGroup entries
881 """
882 oid: str = 'AEVisibleGroups-oid'
883 desc: str = 'AE-DIR: DN of visible user group entry'
884 always_add_groups = (
885 'aeLoginGroups',
886 'aeDisplayNameGroups',
887 )
888
889 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
890 attr_values = set(attr_values)
891 for attr_type in self.always_add_groups:
892 attr_values.update(self._entry.get(attr_type, []))
893 return list(attr_values)
894
895syntax_registry.reg_at(
896 AEVisibleGroups.oid, [
897 AE_OID_PREFIX+'.4.20', # aeVisibleGroups
898 ]
899)
900
901
903 """
904 Plugin class for attributes storing DN references limited to reference
905 entries within the same zone
906 """
907 oid: str = 'AESameZoneObject-oid'
908 desc: str = 'AE-DIR: DN of referenced aeSrvGroup entry this is proxy for'
909 input_fallback = False # no fallback to normal input field
910 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeObject)(aeStatus=0))'
911
912 def _search_root(self):
913 return self._get_zone_dn()
914
915
917 """
918 Plugin for attributes holding DNs of aeSrvGroup entries
919 """
920 oid: str = 'AESrvGroupDN-oid'
921 desc: str = 'AE-DIR: DN of a referenced aeSrvGroup entry'
922 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeSrvGroup)(aeStatus=0))'
923 ref_attrs = DerefDynamicDNSelectList.ref_attrs
924
925
927 """
928 Plugin class for attribute 'aeSrvGroup' in aeUser and aeService entries
929 """
930 oid: str = 'AESrvGroup-oid'
931 desc: str = 'AE-DIR: DN of supplemental aeSrvGroup entry'
932 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeSrvGroup)(aeStatus=0)(!(aeProxyFor=*)))'
933
934 def _filterstr(self):
935 filter_str = self.lu_obj.filterstr or '(objectClass=aeSrvGroup)'
936 return '(&%s(!(entryDN=%s)))' % (
937 filter_str,
938 escape_filter_str(str(self.dn.parent())),
939 )
940
941syntax_registry.reg_at(
942 AESrvGroup.oid, [
943 AE_OID_PREFIX+'.4.27', # aeSrvGroup
944 ]
945)
946
947
949 """
950 Plugin class for attribute 'aeRequires' in aeSrvGroup entries
951 """
952 oid: str = 'AERequires-oid'
953 desc: str = 'AE-DIR: DN of required aeSrvGroup'
954 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeSrvGroup)(aeStatus=0))'
955 ref_attrs = (
956 (
957 'aeRequires', 'Same require', None, 'aeSrvGroup',
958 'Search all service groups depending on this service group.'
959 ),
960 )
961
962syntax_registry.reg_at(
963 AERequires.oid, [
964 AE_OID_PREFIX+'.4.48', # aeRequires
965 ]
966)
967
968
970 """
971 Plugin class for attribute 'aeProxyFor' in aeSrvGroup entries
972 """
973 oid: str = 'AEProxyFor-oid'
974 desc: str = 'AE-DIR: DN of referenced aeSrvGroup entry this is proxy for'
975 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeSrvGroup)(aeStatus=0)(!(aeProxyFor=*)))'
976
977 def _filterstr(self):
978 filter_str = self.lu_obj.filterstr or '(objectClass=*)'
979 return '(&%s(!(entryDN=%s)))' % (
980 filter_str,
981 escape_filter_str(self._dn),
982 )
983
984syntax_registry.reg_at(
985 AEProxyFor.oid, [
986 AE_OID_PREFIX+'.4.25', # aeProxyFor
987 ]
988)
989
990
992 """
993 Plugin class for attribute 'aeTag' in all aeObject entries
994 """
995 oid: str = 'AETag-oid'
996 desc: str = 'AE-DIR: cn of referenced aeTag entry'
997 ldap_url = 'ldap:///_?cn,cn?sub?(&(objectClass=aeTag)(aeStatus=0))'
998
999syntax_registry.reg_at(
1000 AETag.oid, [
1001 AE_OID_PREFIX+'.4.24', # aeTag
1002 ]
1003)
1004
1005
1007 """
1008 Plugin class for attribute 'entryDN' in aePerson entries
1009 """
1010 oid: str = 'AEEntryDNAEPerson-oid'
1011 desc: str = 'AE-DIR: entryDN of aePerson entry'
1012 ref_attrs = (
1013 ('manager', 'Manages', None, 'Search all entries managed by this person'),
1014 (
1015 'aePerson', 'Users', None, 'aeUser',
1016 'Search all personal AE-DIR user accounts (aeUser entries) of this person.'
1017 ),
1018 (
1019 'aeOwner', 'Devices', None, 'aeDevice',
1020 'Search all devices (aeDevice entries) assigned to this person.'
1021 ),
1022 )
1023
1024syntax_registry.reg_at(
1025 AEEntryDNAEPerson.oid, [
1026 '1.3.6.1.1.20', # entryDN
1027 ],
1028 structural_oc_oids=[
1029 AE_PERSON_OID, # aePerson
1030 ],
1031)
1032
1033
1035 """
1036 Plugin class for attribute 'entryDN' in aeUser entries
1037 """
1038 oid: str = 'AEEntryDNAEUser-oid'
1039 desc: str = 'AE-DIR: entryDN of aeUser entry'
1040
1042 res = DistinguishedName._additional_links(self)
1043 res.append(self._app.anchor(
1044 'searchform', 'Created/Modified',
1045 (
1046 ('dn', self._dn),
1047 ('search_root', str(self._app.naming_context)),
1048 ('searchform_mode', 'adv'),
1049 ('search_mode', '(|%s)'),
1050 ('search_attr', 'creatorsName'),
1051 ('search_option', SEARCH_OPT_IS_EQUAL),
1052 ('search_string', self.av_u),
1053 ('search_attr', 'modifiersName'),
1054 ('search_option', SEARCH_OPT_IS_EQUAL),
1055 ('search_string', self.av_u),
1056 ),
1057 title='Search entries created or modified by %s' % (self.av_u),
1058 ))
1059 if self._app.audit_context:
1060 res.append(self._app.anchor(
1061 'search', 'Activity',
1062 (
1063 ('dn', self._app.audit_context),
1064 ('searchform_mode', 'adv'),
1065 ('search_attr', 'objectClass'),
1066 ('search_option', SEARCH_OPT_IS_EQUAL),
1067 ('search_string', 'auditObject'),
1068 ('search_attr', 'reqAuthzID'),
1069 ('search_option', SEARCH_OPT_IS_EQUAL),
1070 ('search_string', self.av_u),
1071 ),
1072 title='Search modifications made by %s in accesslog DB' % (self.av_u),
1073 ))
1074 return res
1075
1076syntax_registry.reg_at(
1077 AEEntryDNAEUser.oid, [
1078 '1.3.6.1.1.20', # entryDN
1079 ],
1080 structural_oc_oids=[
1081 AE_USER_OID, # aeUser
1082 AE_SERVICE_OID, # aeService
1083 ],
1084)
1085
1086
1088 """
1089 Plugin class for attribute 'entryDN' in aeHost entries
1090 """
1091 oid: str = 'AEEntryDNAEHost-oid'
1092 desc: str = 'AE-DIR: entryDN of aeUser entry'
1093 ref_attrs = (
1094 ('aeHost', 'Services', None, 'aeService', 'Search all services running on this host'),
1095 )
1096
1098 res = DistinguishedName._additional_links(self)
1099 srv_group_assertion_values = [escape_filter_str(str(self.dn.parent()))]
1100 srv_group_assertion_values.extend([
1101 escape_filter_str(av.decode(self._app.ls.charset))
1102 for av in self._entry.get('aeSrvGroup', [])
1103 ])
1104 res.extend([
1105 self._app.anchor(
1106 'search', 'Siblings',
1107 (
1108 ('dn', self._dn),
1109 ('search_root', str(self._app.naming_context)),
1110 ('searchform_mode', 'exp'),
1111 (
1112 'filterstr',
1113 '(&(|(objectClass=aeHost)(objectClass=aeService))(|{0}{1}))'.format(
1114 ''.join([
1115 '(entryDN:dnSubordinateMatch:=%s)' % av
1116 for av in srv_group_assertion_values
1117 ]),
1118 ''.join([
1119 '(aeSrvGroup=%s)' % av
1120 for av in srv_group_assertion_values
1121 ]),
1122 )
1123 ),
1124 ),
1125 title=(
1126 'Search all host entries which are member in '
1127 'at least one common server group(s) with this host'
1128 ),
1129 ),
1130 ])
1131 return res
1132
1133syntax_registry.reg_at(
1134 AEEntryDNAEHost.oid, [
1135 '1.3.6.1.1.20', # entryDN
1136 ],
1137 structural_oc_oids=[
1138 AE_HOST_OID, # aeHost
1139 ],
1140)
1141
1142
1144 """
1145 Plugin class for attribute 'entryDN' in aeZone entries
1146 """
1147 oid: str = 'AEEntryDNAEZone-oid'
1148 desc: str = 'AE-DIR: entryDN of aeZone entry'
1149
1151 res = DistinguishedName._additional_links(self)
1152 if self._app.audit_context:
1153 res.append(self._app.anchor(
1154 'search', 'Audit all',
1155 (
1156 ('dn', self._app.audit_context),
1157 ('searchform_mode', 'adv'),
1158 ('search_attr', 'objectClass'),
1159 ('search_option', SEARCH_OPT_IS_EQUAL),
1160 ('search_string', 'auditObject'),
1161 ('search_attr', 'reqDN'),
1162 ('search_option', SEARCH_OPT_DN_SUBTREE),
1163 ('search_string', self.av_u),
1164 ),
1165 title='Search all audit log entries for sub-tree %s' % (self.av_u),
1166 ))
1167 res.append(self._app.anchor(
1168 'search', 'Audit writes',
1169 (
1170 ('dn', self._app.audit_context),
1171 ('searchform_mode', 'adv'),
1172 ('search_attr', 'objectClass'),
1173 ('search_option', SEARCH_OPT_IS_EQUAL),
1174 ('search_string', 'auditObject'),
1175 ('search_attr', 'reqDN'),
1176 ('search_option', SEARCH_OPT_DN_SUBTREE),
1177 ('search_string', self.av_u),
1178 ),
1179 title='Search audit log entries for write operation within sub-tree %s' % (
1180 self.av_u
1181 ),
1182 ))
1183 return res
1184
1185syntax_registry.reg_at(
1186 AEEntryDNAEZone.oid, [
1187 '1.3.6.1.1.20', # entryDN
1188 ],
1189 structural_oc_oids=[
1190 AE_ZONE_OID, # aeZone
1191 ],
1192)
1193
1194
1196 """
1197 Plugin class for attribute 'entryDN' in aeMailGroup entries
1198 """
1199 oid: str = 'AEEntryDNAEMailGroup-oid'
1200 desc: str = 'AE-DIR: entryDN of aeGroup entry'
1201 ref_attrs = (
1202 ('memberOf', 'Members', None, 'Search all member entries of this mail group'),
1203 (
1204 'aeVisibleGroups', 'Visible', None, 'aeSrvGroup',
1205 'Search all server/service groups (aeSrvGroup)\n'
1206 'on which this mail group is visible'
1207 ),
1208 )
1209
1210syntax_registry.reg_at(
1211 AEEntryDNAEMailGroup.oid, [
1212 '1.3.6.1.1.20', # entryDN
1213 ],
1214 structural_oc_oids=[
1215 AE_MAILGROUP_OID, # aeMailGroup
1216 ],
1217)
1218
1219
1221 """
1222 Plugin class for attribute 'entryDN' in aeGroup entries
1223 """
1224 oid: str = 'AEEntryDNAEGroup-oid'
1225 desc: str = 'AE-DIR: entryDN of aeGroup entry'
1226 ref_attrs = (
1227 ('memberOf', 'Members', None, 'Search all member entries of this user group'),
1228 (
1229 'aeLoginGroups', 'Login', None, 'aeSrvGroup',
1230 'Search all server/service groups (aeSrvGroup)\n'
1231 'on which this user group has login right'
1232 ),
1233 (
1234 'aeLogStoreGroups', 'View Logs', None, 'aeSrvGroup',
1235 'Search all server/service groups (aeSrvGroup)\n'
1236 'on which this user group has log view right'
1237 ),
1238 (
1239 'aeSetupGroups', 'Setup', None, 'aeSrvGroup',
1240 'Search all server/service groups (aeSrvGroup)\n'
1241 'on which this user group has setup/installation rights'
1242 ),
1243 (
1244 'aeVisibleGroups', 'Visible', None, 'aeSrvGroup',
1245 'Search all server/service groups (aeSrvGroup)\n'
1246 'on which this user group is at least visible'
1247 ),
1248 )
1249
1251 aegroup_cn = self._entry['cn'][0].decode(self._app.ls.charset)
1252 ref_attrs = list(AEEntryDNAEGroup.ref_attrs)
1253 if aegroup_cn.endswith('zone-admins'):
1254 ref_attrs.extend([
1255 (
1256 'aeZoneAdmins', 'Zone Admins', None,
1257 'Search all zones (aeZone)\n'
1258 'for which members of this user group act as zone admins'
1259 ),
1260 (
1261 'aePasswordAdmins', 'Password Admins', None,
1262 'Search all zones (aeZone)\n'
1263 'for which members of this user group act as password admins'
1264 ),
1265 ])
1266 if aegroup_cn.endswith('zone-auditors') or aegroup_cn.endswith('zone-admins'):
1267 ref_attrs.append(
1268 (
1269 'aeZoneAuditors', 'Zone Auditors', None,
1270 'Search all zones (aeZone)\n'
1271 'for which members of this user group act as zone auditors'
1272 ),
1273 )
1274 self.ref_attrsref_attrsref_attrs = tuple(ref_attrs)
1275 res = DistinguishedName._additional_links(self)
1276 res.append(self._app.anchor(
1277 'search', 'SUDO rules',
1278 (
1279 ('dn', self._dn),
1280 ('search_root', str(self._app.naming_context)),
1281 ('searchform_mode', 'adv'),
1282 ('search_attr', 'sudoUser'),
1283 ('search_option', SEARCH_OPT_IS_EQUAL),
1284 ('search_string', '%'+self._entry['cn'][0].decode(self._app.ls.charset)),
1285 ),
1286 title='Search for SUDO rules\napplicable with this user group',
1287 ))
1288 return res
1289
1290syntax_registry.reg_at(
1291 AEEntryDNAEGroup.oid, [
1292 '1.3.6.1.1.20', # entryDN
1293 ],
1294 structural_oc_oids=[
1295 AE_GROUP_OID, # aeGroup
1296 ],
1297)
1298
1299
1301 """
1302 Plugin class for attribute 'entryDN' in aeSrvGroup entries
1303 """
1304 oid: str = 'AEEntryDNAESrvGroup-oid'
1305 desc: str = 'AE-DIR: entryDN'
1306 ref_attrs = (
1307 (
1308 'aeProxyFor', 'Proxy', None, 'aeSrvGroup',
1309 'Search access gateway/proxy group for this server group'
1310 ),
1311 (
1312 'aeRequires', 'Required by', None, 'aeSrvGroup',
1313 'Search all service groups depending on this service group.'
1314 ),
1315 )
1316
1318 res = DistinguishedName._additional_links(self)
1319 res.append(
1320 self._app.anchor(
1321 'search', 'All members',
1322 (
1323 ('dn', self._dn),
1324 ('search_root', str(self._app.naming_context)),
1325 ('searchform_mode', 'exp'),
1326 (
1327 'filterstr',
1328 (
1329 '(&'
1330 '(|(objectClass=aeHost)(objectClass=aeService))'
1331 '(|(entryDN:dnSubordinateMatch:={0})(aeSrvGroup={0}))'
1332 ')'
1333 ).format(self.av_u)
1334 ),
1335 ),
1336 title=(
1337 'Search all service and host entries '
1338 'which are member in this service/host group {0}'
1339 ).format(self.av_u),
1340 )
1341 )
1342 return res
1343
1344syntax_registry.reg_at(
1345 AEEntryDNAESrvGroup.oid, [
1346 '1.3.6.1.1.20', # entryDN
1347 ],
1348 structural_oc_oids=[
1349 AE_SRVGROUP_OID, # aeSrvGroup
1350 ],
1351)
1352
1353
1355 """
1356 Plugin class for attribute 'entryDN' in aeSudoRule entries
1357 """
1358 oid: str = 'AEEntryDNSudoRule-oid'
1359 desc: str = 'AE-DIR: entryDN'
1360 ref_attrs = (
1361 (
1362 'aeVisibleSudoers', 'Used on', None, 'aeSrvGroup',
1363 'Search all server groups (aeSrvGroup) referencing this SUDO rule'
1364 ),
1365 )
1366
1367syntax_registry.reg_at(
1368 AEEntryDNSudoRule.oid, [
1369 '1.3.6.1.1.20', # entryDN
1370 ],
1371 structural_oc_oids=[
1372 AE_SUDORULE_OID, # aeSudoRule
1373 ],
1374)
1375
1376
1378 """
1379 Plugin class for attribute 'entryDN' in aeLocation entries
1380 """
1381 oid: str = 'AEEntryDNAELocation-oid'
1382 desc: str = 'AE-DIR: entryDN of aeLocation entry'
1383 ref_attrs = (
1384 (
1385 'aeLocation', 'Persons', None, 'aePerson',
1386 'Search all persons assigned to this location.'
1387 ),
1388 (
1389 'aeLocation', 'Zones', None, 'aeZone',
1390 'Search all location-based zones associated with this location.'
1391 ),
1392 (
1393 'aeLocation', 'Groups', None, 'groupOfEntries',
1394 'Search all location-based zones associated with this location.'
1395 ),
1396 )
1397
1398syntax_registry.reg_at(
1399 AEEntryDNAELocation.oid, [
1400 '1.3.6.1.1.20', # entryDN
1401 ],
1402 structural_oc_oids=[
1403 AE_LOCATION_OID, # aeLocation
1404 ],
1405)
1406
1407
1409 """
1410 Plugin class for attribute 'aeLocation' in various entries
1411 """
1412 oid: str = 'AELocation-oid'
1413 desc: str = 'AE-DIR: DN of location entry'
1414 ldap_url = 'ldap:///_?displayName?sub?(&(objectClass=aeLocation)(aeStatus=0))'
1415 ref_attrs = AEEntryDNAELocation.ref_attrs
1416 desc_sep: str = '<br>'
1417
1418syntax_registry.reg_at(
1419 AELocation.oid, [
1420 AE_OID_PREFIX+'.4.35', # aeLocation
1421 ]
1422)
1423
1424
1426 """
1427 Plugin class for attribute 'entryDN' in aeDept entries
1428 """
1429 oid: str = 'AEEntryDNAEDept-oid'
1430 desc: str = 'AE-DIR: entryDN of aePerson entry'
1431 ref_attrs = (
1432 (
1433 'aeDept', 'Persons', None, 'aePerson',
1434 'Search all persons assigned to this department.'
1435 ),
1436 (
1437 'aeDept', 'Zones', None, 'aeZone',
1438 'Search all team-related zones associated with this department.'
1439 ),
1440 (
1441 'aeDept', 'Groups', None, 'groupOfEntries',
1442 'Search all team-related groups associated with this department.'
1443 ),
1444 )
1445
1446syntax_registry.reg_at(
1447 AEEntryDNAEDept.oid, [
1448 '1.3.6.1.1.20', # entryDN
1449 ],
1450 structural_oc_oids=[
1451 AE_DEPT_OID, # aeDept
1452 ],
1453)
1454
1455
1457 """
1458 Plugin class for attribute 'aeDept' in various entries
1459 """
1460 oid: str = 'AEDept-oid'
1461 desc: str = 'AE-DIR: DN of department entry'
1462 ldap_url = 'ldap:///_?displayName?sub?(&(objectClass=aeDept)(aeStatus=0))'
1463 ref_attrs = AEEntryDNAEDept.ref_attrs
1464 desc_sep: str = '<br>'
1465
1466syntax_registry.reg_at(
1467 AEDept.oid, [
1468 AE_OID_PREFIX+'.4.29', # aeDept
1469 ]
1470)
1471
1472
1474 """
1475 Plugin class for attribute 'aeOwner' in aeDevice and aeSession entries
1476 """
1477 oid: str = 'AEOwner-oid'
1478 desc: str = 'AE-DIR: DN of owner entry'
1479 ldap_url = 'ldap:///_?displayName?sub?(&(objectClass=aePerson)(aeStatus=0))'
1480 ref_attrs = (
1481 (
1482 'aeOwner', 'Devices', None, 'aeDevice',
1483 'Search all devices (aeDevice entries) assigned to same owner.'
1484 ),
1485 )
1486 desc_sep: str = '<br>'
1487
1488syntax_registry.reg_at(
1489 AEOwner.oid, [
1490 AE_OID_PREFIX+'.4.2', # aeOwner
1491 ]
1492)
1493
1494
1496 """
1497 Plugin class for attribute 'aePerson' in aeUser entries
1498 """
1499 oid: str = 'AEPerson-oid'
1500 desc: str = 'AE-DIR: DN of person entry'
1501 ldap_url = 'ldap:///_?displayName?sub?(objectClass=aePerson)'
1502 ref_attrs = (
1503 (
1504 'aePerson', 'Users', None, 'aeUser',
1505 'Search all personal AE-DIR user accounts (aeUser entries) of this person.'
1506 ),
1507 )
1508 desc_sep: str = '<br>'
1509 ae_status_map = {
1510 -1: (-1, 0),
1511 0: (0,),
1512 1: (0, 1, 2),
1513 2: (0, 1, 2),
1514 }
1515 deref_attrs = ('aeDept', 'aeLocation')
1516
1518 ae_status = self.ae_statusae_status or 0
1519 return compose_filter(
1520 '|',
1521 map_filter_parts(
1522 'aeStatus',
1523 map(str, self.ae_status_map.get(ae_status, [])),
1524 ),
1525 )
1526
1527 def _filterstr(self):
1528 filter_components = [
1529 DerefDynamicDNSelectList._filterstr(self),
1530 self._status_filter(),
1531 #ae_validity_filter(),
1532 ]
1533 zone_entry = self._zone_entry(attrlist=self.deref_attrs)
1534 for deref_attr_type in self.deref_attrs:
1535 deref_attr_values = [
1536 z
1537 for z in zone_entry.get(deref_attr_type, [])
1538 if z
1539 ]
1540 if deref_attr_values:
1541 filter_components.append(
1542 compose_filter(
1543 '|',
1544 map_filter_parts(deref_attr_type, deref_attr_values),
1545 )
1546 )
1547 ocs = self._entry.object_class_oid_set()
1548 if 'inetLocalMailRecipient' not in ocs:
1549 filter_components.append('(mail=*)')
1550 filter_str = '(&{})'.format(''.join(filter_components))
1551 return filter_str
1552
1553 def _validate(self, attr_value: bytes) -> bool:
1555 return True
1556 return DerefDynamicDNSelectList._validate(self, attr_value)
1557
1558
1559syntax_registry.reg_at(
1560 AEPerson.oid, [
1561 AE_OID_PREFIX+'.4.16', # aePerson
1562 ]
1563)
1564
1565
1567 """
1568 Plugin class for attribute 'aeManager' in aePerson and aeDept entries
1569 """
1570 oid: str = 'AEManager-oid'
1571 desc: str = 'AE-DIR: Manager responsible for a person/department'
1572 ldap_url = 'ldap:///_?displayName?sub?(&(objectClass=aePerson)(aeStatus=0))'
1573 desc_sep: str = '<br>'
1574
1575syntax_registry.reg_at(
1576 AEManager.oid, [
1577 '0.9.2342.19200300.100.1.10', # manager
1578 ],
1579 structural_oc_oids=[
1580 AE_PERSON_OID, # aePerson
1581 AE_DEPT_OID, # aeDept
1582 ]
1583)
1584
1585
1587 """
1588 Plugin class for attributes referencing other entries
1589 """
1590 oid: str = 'AEDerefAttribute-oid'
1591 max_values: int = 1
1592 deref_object_class: Optional[str] = None
1593 deref_attribute_type: Optional[str] = None
1594 deref_filter_tmpl: str = (
1595 '(&(objectClass={deref_object_class})(aeStatus<=0)({attribute_type}=*))'
1596 )
1597
1599 try:
1600 sre = self._app.ls.l.read_s(
1601 self._entry[self.deref_attribute_type][0].decode(self._app.ls.charset),
1602 attrlist=[self._at],
1603 filterstr=self.deref_filter_tmpl.format(
1604 deref_object_class=self.deref_object_class,
1605 attribute_type=self._at,
1606 ),
1607 )
1608 except ldap0.LDAPError:
1609 return None
1610 if sre is None:
1611 return None
1612 return sre.entry_s[self._at][0]
1613
1614 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
1615 if self.deref_attribute_type in self._entry:
1616 ae_person_attribute = self._read_person_attr()
1617 if ae_person_attribute is not None:
1618 result = [ae_person_attribute.encode(self._app.ls.charset)]
1619 else:
1620 result = []
1621 else:
1622 result = attr_values
1623 return result
1624
1625 def form_value(self) -> str:
1626 return ''
1627
1628 def input_field(self) -> Field:
1629 input_field = HiddenInput(
1630 self._at,
1631 ': '.join([self._at, self.desc]),
1632 self.max_len, self.max_values, None,
1633 )
1634 input_field.charset = self._app.form.accept_charset
1635 input_field.set_default(self.form_valueform_value())
1636 return input_field
1637
1638
1640 """
1641 Plugin class for aeUser attributes copied from referenced aePerson entries
1642 """
1643 oid: str = 'AEPersonAttribute-oid'
1644 max_values = 1
1645 deref_object_class = 'aePerson'
1646 deref_attribute_type = 'aePerson'
1647
1648
1650 """
1651 Plugin class for aeUser attributes 'sn' and 'givenName' copied
1652 from referenced aePerson entries
1653 """
1654 oid: str = 'AEUserNames-oid'
1655
1656syntax_registry.reg_at(
1657 AEUserNames.oid, [
1658 '2.5.4.4', # sn
1659 '2.5.4.42', # givenName
1660 ],
1661 structural_oc_oids=[
1662 AE_USER_OID, # aeUser
1663 ],
1664)
1665
1666
1668 """
1669 Plugin class for attribute 'mailLocalAddress' in aeUser and aeService entries
1670 """
1671 oid: str = 'AEMailLocalAddress-oid'
1672 sani_funcs = (
1673 bytes.strip,
1674 bytes.lower,
1675 )
1676
1677syntax_registry.reg_at(
1678 AEMailLocalAddress.oid, [
1679 '2.16.840.1.113730.3.1.13', # mailLocalAddress
1680 ],
1681 structural_oc_oids=[
1682 AE_USER_OID, # aeUser
1683 AE_SERVICE_OID, # aeService
1684 ],
1685)
1686
1687
1689 """
1690 Plugin class for attribute 'mail' in aeUser entries
1691
1692 For primary mail user accounts this contains one of
1693 the values in attribute 'mailLocalAddress'.
1694 """
1695 oid: str = 'AEUserMailaddress-oid'
1696 max_values = 1
1697 input_fallback = False
1698 sani_funcs = (
1699 bytes.strip,
1700 bytes.lower,
1701 )
1702
1703 def get_attr_value_dict(self) -> Dict[str, str]:
1704 attr_value_dict: Dict[str, str] = {
1705 '': '-/-',
1706 }
1707 for addr in self._entry.get('mailLocalAddress', []):
1708 addr_u = addr.decode(self._app.ls.charset)
1709 attr_value_dict[addr_u] = addr_u
1710 return attr_value_dict
1711
1713 return b'inetLocalMailRecipient' in self._entry['objectClass']
1714
1715 def _validate(self, attr_value: bytes) -> bool:
1716 if self._is_mail_account():
1717 return SelectList._validate(self, attr_value)
1718 return AEPersonAttribute._validate(self, attr_value)
1719
1720 def display(self, vidx, links) -> str:
1721 return RFC822Address.display(self, vidx, links)
1722
1723 def form_value(self) -> str:
1724 if self._is_mail_account():
1725 return SelectList.form_value(self)
1726 return AEPersonAttribute.form_value(self)
1727
1728 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
1729 if self._is_mail_account():
1730 # make sure only non-empty strings are in attribute value list
1731 if not list(filter(None, map(bytes.strip, attr_values))):
1732 try:
1733 attr_values = [self._entry['mailLocalAddress'][0]]
1734 except KeyError:
1735 attr_values = []
1736 else:
1737 attr_values = AEPersonAttribute.transmute(self, attr_values)
1738 return attr_values
1739
1740 def input_field(self) -> Field:
1741 if self._is_mail_account():
1742 return SelectList.input_field(self)
1743 return AEPersonAttribute.input_field(self)
1744
1745syntax_registry.reg_at(
1746 AEUserMailaddress.oid, [
1747 '0.9.2342.19200300.100.1.3', # mail
1748 ],
1749 structural_oc_oids=[
1750 AE_USER_OID, # aeUser
1751 ],
1752)
1753
1754
1756 """
1757 Plugin class for attribute 'mail' in aePerson entries
1758
1759 If there exists a primary mail user account for this person this
1760 contains one of the values in attribute 'mailLocalAddress' in that
1761 aeUser entry.
1762 """
1763 oid: str = 'AEPersonMailaddress-oid'
1764 max_values = 1
1765 ldap_url = 'ldap:///_?mail,mail?sub?'
1766 input_fallback = True
1767 html_tmpl = RFC822Address.html_tmpl
1768
1769 def _validate(self, attr_value: bytes) -> bool:
1770 if not RFC822Address._validate(self, attr_value):
1771 return False
1772 attr_value_dict: Dict[str, str] = self.get_attr_value_dictget_attr_value_dict()
1773 if (
1774 not attr_value_dict
1775 or (
1776 len(attr_value_dict) == 1
1777 and tuple(attr_value_dict.keys()) == ('',)
1778 )
1779 ):
1780 return True
1781 return DynamicValueSelectList._validate(self, attr_value)
1782
1783 def _filterstr(self):
1784 return (
1785 '(&'
1786 '(objectClass=aeUser)'
1787 '(objectClass=inetLocalMailRecipient)'
1788 '(aeStatus=0)'
1789 '(aePerson=%s)'
1790 '(mailLocalAddress=*)'
1791 ')'
1792 ) % escape_filter_str(self._dn)
1793
1794syntax_registry.reg_at(
1795 AEPersonMailaddress.oid, [
1796 '0.9.2342.19200300.100.1.3', # mail
1797 ],
1798 structural_oc_oids=[
1799 AE_PERSON_OID, # aePerson
1800 ],
1801)
1802
1803
1805 """
1806 Plugin class for aePerson attributes copied from referenced aeDept entries
1807 """
1808 oid: str = 'AEDeptAttribute-oid'
1809 max_values = 1
1810 deref_object_class = 'aeDept'
1811 deref_attribute_type = 'aeDept'
1812
1813syntax_registry.reg_at(
1814 AEDeptAttribute.oid, [
1815 '2.16.840.1.113730.3.1.2', # departmentNumber
1816 '2.5.4.11', # ou, organizationalUnitName
1817 ],
1818 structural_oc_oids=[
1819 AE_PERSON_OID, # aePerson
1820 ],
1821)
1822
1823
1825 """
1826 Plugin class for attribute 'host' in aeHost entries
1827 """
1828 oid: str = 'AEHostname-oid'
1829 desc: str = 'Canonical hostname / FQDN'
1830 host_lookup = 0
1831
1832 def _validate(self, attr_value: bytes) -> bool:
1833 if not DNSDomain._validate(self, attr_value):
1834 return False
1835 if self.host_lookup:
1836 try:
1837 ip_addr = socket.gethostbyname(self._app.ls.uc_decode(attr_value)[0])
1838 except (socket.gaierror, socket.herror):
1839 return False
1840 if self.host_lookup >= 2:
1841 try:
1842 reverse_hostname = socket.gethostbyaddr(ip_addr)[0]
1843 except (socket.gaierror, socket.herror):
1844 return False
1845 else:
1846 return reverse_hostname == attr_value
1847 return True
1848
1849 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
1850 result = []
1851 for attr_value in attr_values:
1852 attr_value.lower().strip()
1853 if self.host_lookup:
1854 try:
1855 ip_addr = socket.gethostbyname(self._app.ls.uc_decode(attr_value)[0])
1856 reverse_hostname = socket.gethostbyaddr(ip_addr)[0]
1857 except (socket.gaierror, socket.herror):
1858 pass
1859 else:
1860 attr_value = reverse_hostname.encode(self._app.ls.charset)
1861 result.append(attr_value)
1862 return attr_values
1863
1864syntax_registry.reg_at(
1865 AEHostname.oid, [
1866 '0.9.2342.19200300.100.1.9', # host
1867 ],
1868 structural_oc_oids=[
1869 AE_HOST_OID, # aeHost
1870 ],
1871)
1872
1873
1875 """
1876 Plugin class for attribute 'displayName' in aeUser entries
1877 """
1878 oid: str = 'AEDisplayNameUser-oid'
1879 desc: str = 'Attribute displayName in object class aeUser'
1880 compose_templates = (
1881 '{givenName} {sn} ({uid}/{uidNumber})',
1882 '{givenName} {sn} ({uid})',
1883 )
1884
1885syntax_registry.reg_at(
1886 AEDisplayNameUser.oid, [
1887 '2.16.840.1.113730.3.1.241', # displayName
1888 ],
1889 structural_oc_oids=[AE_USER_OID], # aeUser
1890)
1891
1892
1894 """
1895 Plugin class for attribute 'displayName' in aeContact entries
1896 """
1897 oid: str = 'AEDisplayNameContact-oid'
1898 desc: str = 'Attribute displayName in object class aeContact'
1899 compose_templates = (
1900 '{cn} <{mail}>',
1901 '{cn}',
1902 )
1903
1904syntax_registry.reg_at(
1905 AEDisplayNameContact.oid, [
1906 '2.16.840.1.113730.3.1.241', # displayName
1907 ],
1908 structural_oc_oids=[AE_CONTACT_OID], # aeContact
1909)
1910
1911
1913 """
1914 Plugin class for attribute 'displayName' in aeDept entries
1915 """
1916 oid: str = 'AEDisplayNameDept-oid'
1917 desc: str = 'Attribute displayName in object class aeDept'
1918 compose_templates = (
1919 '{ou} ({departmentNumber})',
1920 '{ou}',
1921 '#{departmentNumber}',
1922 )
1923
1924syntax_registry.reg_at(
1925 AEDisplayNameDept.oid, [
1926 '2.16.840.1.113730.3.1.241', # displayName
1927 ],
1928 structural_oc_oids=[AE_DEPT_OID], # aeDept
1929)
1930
1931
1933 """
1934 Plugin class for attribute 'displayName' in aeLocation entries
1935 """
1936 oid: str = 'AEDisplayNameLocation-oid'
1937 desc: str = 'Attribute displayName in object class aeLocation'
1938 compose_templates = (
1939 '{cn}: {l}, {street}',
1940 '{cn}: {l}',
1941 '{cn}: {street}',
1942 '{cn}: {st}',
1943 '{cn}',
1944 )
1945
1946syntax_registry.reg_at(
1947 AEDisplayNameLocation.oid, [
1948 '2.16.840.1.113730.3.1.241', # displayName
1949 ],
1950 structural_oc_oids=[AE_LOCATION_OID], # aeLocation
1951)
1952
1953
1955 """
1956 Plugin class for attribute 'displayName' in aePerson entries
1957 """
1958 oid: str = 'AEDisplayNamePerson-oid'
1959 desc: str = 'Attribute displayName in object class aePerson'
1960 # do not stuff confidential employeeNumber herein!
1961 compose_templates = (
1962 '{givenName} {sn} / {ou}',
1963 '{givenName} {sn} / #{departmentNumber}',
1964 '{givenName} {sn} ({uniqueIdentifier})',
1965 '{givenName} {sn}',
1966 )
1967
1968syntax_registry.reg_at(
1969 AEDisplayNamePerson.oid, [
1970 '2.16.840.1.113730.3.1.241', # displayName
1971 ],
1972 structural_oc_oids=[AE_PERSON_OID], # aePerson
1973)
1974
1975
1977 """
1978 Plugin class for attribute 'uniqueIdentifier' in aePerson entries
1979 """
1980 oid: str = 'AEUniqueIdentifier-oid'
1981 max_values = 1
1982 gen_template = 'web2ldap-{timestamp}'
1983
1984 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
1985 if not attr_values or not attr_values[0].strip():
1986 return [self.gen_template.format(timestamp=time.time()).encode(self._app.ls.charset)]
1987 return attr_values
1988
1989 def input_field(self) -> Field:
1990 input_field = HiddenInput(
1991 self._at,
1992 ': '.join([self._at, self.desc]),
1993 self.max_len, self.max_values, None,
1994 default=self.form_value(),
1995 )
1996 input_field.charset = self._app.form.accept_charset
1997 return input_field
1998
1999syntax_registry.reg_at(
2000 AEUniqueIdentifier.oid, [
2001 '0.9.2342.19200300.100.1.44', # uniqueIdentifier
2002 ],
2003 structural_oc_oids=[
2004 AE_PERSON_OID, # aePerson
2005 ]
2006)
2007
2008
2010 """
2011 Plugin class for attribute 'departmentNumber' in aeDept entries
2012 """
2013 oid: str = 'AEDepartmentNumber-oid'
2014 max_values = 1
2015
2016syntax_registry.reg_at(
2017 AEDepartmentNumber.oid, [
2018 '2.16.840.1.113730.3.1.2', # departmentNumber
2019 ],
2020 structural_oc_oids=[
2021 AE_DEPT_OID, # aeDept
2022 ]
2023)
2024
2025
2027 """
2028 Base class for all plugin classes handling 'cn' in xC6-DIR plugin classes,
2029 not directly used
2030 """
2031 oid: str = 'AECommonName-oid'
2032 desc: str = 'AE-DIR: common name of aeObject'
2033 max_values = 1
2034 sani_funcs = (
2035 bytes.strip,
2036 )
2037
2038
2040 """
2041 Plugin for attribute 'cn' in aeZone entries
2042 """
2043 oid: str = 'AECommonNameAEZone-oid'
2044 desc: str = 'AE-DIR: common name of aeZone'
2045 sani_funcs = (
2046 bytes.strip,
2047 bytes.lower,
2048 )
2049
2050syntax_registry.reg_at(
2051 AECommonNameAEZone.oid, [
2052 '2.5.4.3', # cn alias commonName
2053 ],
2054 structural_oc_oids=[
2055 AE_ZONE_OID, # aeZone
2056 ],
2057)
2058
2059
2061 """
2062 Plugin for attribute 'cn' in aeLocation entries
2063 """
2064 oid: str = 'AECommonNameAELocation-oid'
2065 desc: str = 'AE-DIR: common name of aeLocation'
2066
2067syntax_registry.reg_at(
2068 AECommonNameAELocation.oid, [
2069 '2.5.4.3', # cn alias commonName
2070 ],
2071 structural_oc_oids=[
2072 AE_LOCATION_OID, # aeLocation
2073 ],
2074)
2075
2076
2078 """
2079 Plugin for attribute 'cn' in aeHost entries
2080 """
2081 oid: str = 'AECommonNameAEHost-oid'
2082 desc: str = 'Canonical hostname'
2083 derive_from_host = True
2084 host_begin_item = 0
2085 host_end_item = None
2086
2087 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
2088 if self.derive_from_host:
2089 return list({
2090 b'.'.join(av.strip().lower().split(b'.')[self.host_begin_item:self.host_end_item])
2091 for av in self._entry['host']
2092 })
2093 return attr_values
2094
2095syntax_registry.reg_at(
2096 AECommonNameAEHost.oid, [
2097 '2.5.4.3', # cn alias commonName
2098 ],
2099 structural_oc_oids=[
2100 AE_HOST_OID, # aeHost
2101 ],
2102)
2103
2104
2106 """
2107 Base class for handling 'cn' in entries which must have zone name as prefix
2108 """
2109 oid: str = 'AEZonePrefixCommonName-oid'
2110 desc: str = 'AE-DIR: Attribute values have to be prefixed with zone name'
2111 pattern = re.compile(r'^[a-z0-9]+-[a-z0-9-]+$')
2112 special_names = {
2113 'zone-admins',
2114 'zone-auditors',
2115 }
2116
2117 def sanitize(self, attr_value: bytes) -> bytes:
2118 return attr_value.strip()
2119
2120 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
2121 attr_values = [attr_values[0].lower()]
2122 return attr_values
2123
2124 def _validate(self, attr_value: bytes) -> bool:
2125 result = DirectoryString._validate(self, attr_value)
2126 if result and attr_value:
2127 zone_cn = self._get_zone_name()
2128 result = (
2129 zone_cn and
2130 (
2131 zone_cn == 'pub'
2132 or attr_value.decode(self._app.ls.charset).startswith(zone_cn+'-')
2133 )
2134 )
2135 return result
2136
2137 def form_value(self) -> str:
2138 result = DirectoryString.form_value(self)
2139 zone_cn = self._get_zone_name()
2140 if zone_cn:
2141 if not self._av:
2142 result = zone_cn+'-'
2143 elif self._av_u in self.special_names:
2144 result = '-'.join((zone_cn, self.av_u))
2145 return result
2146
2147
2149 """
2150 Plugin for attribute 'cn' in aeGroup entries
2151 """
2152 oid: str = 'AECommonNameAEGroup-oid'
2153
2154syntax_registry.reg_at(
2155 AECommonNameAEGroup.oid, [
2156 '2.5.4.3', # cn alias commonName
2157 ],
2158 structural_oc_oids=[
2159 AE_GROUP_OID, # aeGroup
2160 AE_MAILGROUP_OID, # aeMailGroup
2161 ]
2162)
2163
2164
2166 """
2167 Plugin for attribute 'cn' in aeSrvGroup entries
2168 """
2169 oid: str = 'AECommonNameAESrvGroup-oid'
2170
2171syntax_registry.reg_at(
2172 AECommonNameAESrvGroup.oid, [
2173 '2.5.4.3', # cn alias commonName
2174 ],
2175 structural_oc_oids=[
2176 AE_SRVGROUP_OID, # aeSrvGroup
2177 ]
2178)
2179
2180
2182 """
2183 Plugin for attribute 'cn' in aeTag entries
2184 """
2185 oid: str = 'AECommonNameAETag-oid'
2186
2187 def display(self, vidx, links) -> str:
2188 display_value = AEZonePrefixCommonName.display(self, vidx, links)
2189 if links:
2190 search_anchor = self._app.anchor(
2191 'searchform', '&raquo;',
2192 (
2193 ('dn', self._dn),
2194 ('search_root', str(self._app.naming_context)),
2195 ('searchform_mode', 'adv'),
2196 ('search_attr', 'aeTag'),
2197 ('search_option', SEARCH_OPT_IS_EQUAL),
2198 ('search_string', self.av_u),
2199 ),
2200 title='Search all entries tagged with this tag',
2201 )
2202 else:
2203 search_anchor = ''
2204 return ''.join((display_value, search_anchor))
2205
2206syntax_registry.reg_at(
2207 AECommonNameAETag.oid, [
2208 '2.5.4.3', # cn alias commonName
2209 ],
2210 structural_oc_oids=[
2211 AE_TAG_OID, # aeTag
2212 ]
2213)
2214
2215
2217 """
2218 Plugin for attribute 'cn' in aeSudoRule entries
2219 """
2220 oid: str = 'AECommonNameAESudoRule-oid'
2221
2222syntax_registry.reg_at(
2223 AECommonNameAESudoRule.oid, [
2224 '2.5.4.3', # cn alias commonName
2225 ],
2226 structural_oc_oids=[
2227 AE_SUDORULE_OID, # aeSudoRule
2228 ]
2229)
2230
2231syntax_registry.reg_at(
2232 CNInetOrgPerson.oid, [
2233 '2.5.4.3', # commonName
2234 ],
2235 structural_oc_oids=[
2236 AE_PERSON_OID, # aePerson
2237 AE_USER_OID, # aeUser
2238 ]
2239)
2240
2241
2243 """
2244 Plugin for attribute 'aeVisibleSudoers' in aeSrvGroup entries
2245 """
2246 oid: str = 'AESudoRuleDN-oid'
2247 desc: str = 'AE-DIR: DN(s) of visible SUDO rules'
2248 ldap_url = 'ldap:///_?cn?sub?(&(objectClass=aeSudoRule)(aeStatus=0))'
2249
2250syntax_registry.reg_at(
2251 AESudoRuleDN.oid, [
2252 AE_OID_PREFIX+'.4.21', # aeVisibleSudoers
2253 ]
2254)
2255
2256
2258 """
2259 Plugin for attribute 'aeNotBefore' in all aeObject entries
2260 """
2261 oid: str = 'AENotBefore-oid'
2262 desc: str = 'AE-DIR: begin of validity period'
2263
2264syntax_registry.reg_at(
2265 AENotBefore.oid, [
2266 AE_OID_PREFIX+'.4.22', # aeNotBefore
2267 ]
2268)
2269
2270
2272 """
2273 Plugin for attribute 'aeNotAfter' in all aeObject entries
2274 """
2275 oid: str = 'AENotAfter-oid'
2276 desc: str = 'AE-DIR: begin of validity period'
2277
2278 def _validate(self, attr_value: bytes) -> bool:
2279 result = NotAfter._validate(self, attr_value)
2280 if result:
2281 ae_not_after = time.strptime(attr_value.decode('ascii'), '%Y%m%d%H%M%SZ')
2282 if (
2283 'aeNotBefore' not in self._entry
2284 or not self._entry['aeNotBefore']
2285 or not self._entry['aeNotBefore'][0]
2286 ):
2287 return True
2288 try:
2289 ae_not_before = time.strptime(
2290 self._entry['aeNotBefore'][0].decode('ascii'),
2291 '%Y%m%d%H%M%SZ',
2292 )
2293 except KeyError:
2294 result = True
2295 except (UnicodeDecodeError, ValueError):
2296 result = False
2297 else:
2298 result = (ae_not_before <= ae_not_after)
2299 return result
2300
2301syntax_registry.reg_at(
2302 AENotAfter.oid, [
2303 AE_OID_PREFIX+'.4.23', # aeNotAfter
2304 ]
2305)
2306
2307
2309 """
2310 Plugin for attribute 'aeStatus' in all aeObject entries
2311 """
2312 oid: str = 'AEStatus-oid'
2313 desc: str = 'AE-DIR: Status of object'
2314 attr_value_dict: Dict[str, str] = {
2315 '-1': 'requested',
2316 '0': 'active',
2317 '1': 'deactivated',
2318 '2': 'archived',
2319 }
2320
2321 def _validate(self, attr_value: bytes) -> bool:
2322 result = SelectList._validate(self, attr_value)
2323 if not result or not attr_value:
2324 return result
2325 ae_status = int(attr_value)
2326 current_time = time.gmtime(time.time())
2327 try:
2328 ae_not_before = time.strptime(
2329 self._entry['aeNotBefore'][0].decode('ascii'),
2330 '%Y%m%d%H%M%SZ',
2331 )
2332 except (KeyError, IndexError, ValueError, UnicodeDecodeError):
2333 ae_not_before = time.strptime('19700101000000Z', '%Y%m%d%H%M%SZ')
2334 try:
2335 ae_not_after = time.strptime(
2336 self._entry['aeNotAfter'][0].decode('ascii'),
2337 '%Y%m%d%H%M%SZ',
2338 )
2339 except (KeyError, IndexError, ValueError, UnicodeDecodeError):
2340 ae_not_after = current_time
2341 # see https://www.ae-dir.com/docs.html#schema-validity-period
2342 if current_time > ae_not_after:
2343 result = ae_status >= 1
2344 elif current_time < ae_not_before:
2345 result = ae_status == -1
2346 else:
2347 result = ae_not_before <= current_time <= ae_not_after
2348 return result
2349
2350 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
2351 if not attr_values or not attr_values[0]:
2352 return attr_values
2353 ae_status = int(attr_values[0].decode('ascii'))
2354 current_time = time.gmtime(time.time())
2355 try:
2356 ae_not_before = time.strptime(
2357 self._entry['aeNotBefore'][0].decode('ascii'),
2358 '%Y%m%d%H%M%SZ',
2359 )
2360 except (KeyError, IndexError, ValueError):
2361 pass
2362 else:
2363 if ae_status == 0 and current_time < ae_not_before:
2364 ae_status = -1
2365 try:
2366 ae_not_after = time.strptime(
2367 self._entry['aeNotAfter'][0].decode('ascii'),
2368 '%Y%m%d%H%M%SZ',
2369 )
2370 except (KeyError, IndexError, ValueError):
2371 ae_not_after = None
2372 else:
2373 if current_time > ae_not_after:
2374 try:
2375 ae_expiry_status = int(
2376 self._entry.get('aeExpiryStatus', ['1'])[0].decode('ascii')
2377 )
2378 except (KeyError, IndexError, ValueError):
2379 pass
2380 else:
2381 ae_status = max(ae_status, ae_expiry_status)
2382 return [str(ae_status).encode('ascii')]
2383
2384 def display(self, vidx, links) -> str:
2385 if not links:
2386 return Integer.display(self, vidx, links)
2387 return SelectList.display(self, vidx, links)
2388
2389syntax_registry.reg_at(
2390 AEStatus.oid, [
2391 AE_OID_PREFIX+'.4.5', # aeStatus
2392 ]
2393)
2394
2395
2397 """
2398 Plugin for attribute 'aeExpiryStatus' in all aeObject entries
2399 """
2400 oid: str = 'AEExpiryStatus-oid'
2401 desc: str = 'AE-DIR: Expiry status of object'
2402 attr_value_dict: Dict[str, str] = {
2403 '-/-': '',
2404 '1': 'deactivated',
2405 '2': 'archived',
2406 }
2407
2408syntax_registry.reg_at(
2409 AEStatus.oid, [
2410 AE_OID_PREFIX+'.4.46', # aeExpiryStatus
2411 ]
2412)
2413
2414
2416 """
2417 Plugin for attribute 'sudoUser' in aeSudoRule entries
2418 """
2419 oid: str = 'AESudoUser-oid'
2420 desc: str = 'AE-DIR: sudoUser'
2421 ldap_url = (
2422 'ldap:///_?cn,cn?sub?'
2423 '(&'
2424 '(objectClass=aeGroup)'
2425 '(aeStatus=0)'
2426 '(!(|'
2427 '(cn=ae-admins)'
2428 '(cn=ae-auditors)'
2429 '(cn=ae-providers)'
2430 '(cn=ae-replicas)'
2431 '(cn=ae-login-proxies)'
2432 '(cn=*-zone-admins)'
2433 '(cn=*-zone-auditors)'
2434 '))'
2435 ')'
2436 )
2437
2438syntax_registry.reg_at(
2439 AESudoUser.oid, [
2440 '1.3.6.1.4.1.15953.9.1.1', # sudoUser
2441 ],
2442 structural_oc_oids=[
2443 AE_SUDORULE_OID, # aeSudoRule
2444 ]
2445)
2446
2447
2449 """
2450 Plugin for attribute 'sshPublicKey' in aeService entries
2451
2452 Mainly this can be used to assign specific regex pattern
2453 e.g. for limiting values to certain OpenSSH key types
2454 in aeService entries.
2455 """
2456 oid: str = 'AEServiceSshPublicKey-oid'
2457 desc: str = 'AE-DIR: aeService:sshPublicKey'
2458
2459syntax_registry.reg_at(
2460 AEServiceSshPublicKey.oid, [
2461 '1.3.6.1.4.1.24552.500.1.1.1.13', # sshPublicKey
2462 ],
2463 structural_oc_oids=[
2464 AE_SERVICE_OID, # aeService
2465 ]
2466)
2467
2468
2470 """
2471 Plugin for attribute 'sshPublicKey' in aeUser entries
2472
2473 Mainly this can be used to assign specific regex pattern
2474 e.g. for limiting values to certain OpenSSH key types
2475 in aeUser entries.
2476 """
2477 oid: str = 'AEUserSshPublicKey-oid'
2478 desc: str = 'AE-DIR: aeUser:sshPublicKey'
2479
2480syntax_registry.reg_at(
2481 AEUserSshPublicKey.oid, [
2482 '1.3.6.1.4.1.24552.500.1.1.1.13', # sshPublicKey
2483 ],
2484 structural_oc_oids=[
2485 AE_USER_OID, # aeUser
2486 ]
2487)
2488
2489
2491 """
2492 Plugin for attribute 'entryDN' in aeAuthcToken entries
2493 """
2494 oid: str = 'AEEntryDNAEAuthcToken-oid'
2495 desc: str = 'AE-DIR: entryDN of aeAuthcToken entry'
2496 ref_attrs = (
2497 (
2498 'oathToken', 'Users', None, 'aeUser',
2499 'Search all personal user accounts using this OATH token.'
2500 ),
2501 )
2502
2503syntax_registry.reg_at(
2504 AEEntryDNAEAuthcToken.oid, [
2505 '1.3.6.1.1.20', # entryDN
2506 ],
2507 structural_oc_oids=[
2508 AE_AUTHCTOKEN_OID, # aeAuthcToken
2509 ],
2510)
2511
2512
2514 """
2515 Plugin for attribute 'entryDN' in aePolicy entries
2516 """
2517 oid: str = 'AEEntryDNAEPolicy-oid'
2518 desc: str = 'AE-DIR: entryDN of aePolicy entry'
2519 ref_attrs = (
2520 (
2521 'pwdPolicySubentry', 'Users', None, 'aeUser',
2522 'Search all personal user accounts restricted by this password policy.'
2523 ),
2524 (
2525 'pwdPolicySubentry', 'Services', None, 'aeService',
2526 'Search all service accounts restricted by this password policy.'
2527 ),
2528 (
2529 'pwdPolicySubentry', 'Tokens', None, 'aeAuthcToken',
2530 'Search all authentication tokens restricted by this password policy.'
2531 ),
2532 (
2533 'oathHOTPParams', 'HOTP Tokens', None, 'oathHOTPToken',
2534 'Search all HOTP tokens affected by this HOTP parameters.'
2535 ),
2536 (
2537 'oathTOTPParams', 'TOTP Tokens', None, 'oathTOTPToken',
2538 'Search all TOTP tokens affected by this TOTP parameters.'
2539 ),
2540 )
2541
2542syntax_registry.reg_at(
2543 AEEntryDNAEPolicy.oid, [
2544 '1.3.6.1.1.20', # entryDN
2545 ],
2546 structural_oc_oids=[
2547 AE_POLICY_OID, # aePolicy
2548 ],
2549)
2550
2551
2553 """
2554 Plugin for attribute 'rfc822MailMember' in aeMailGroup entries
2555 """
2556 oid: str = 'AERFC822MailMember-oid'
2557 desc: str = 'AE-DIR: rfc822MailMember'
2558 ldap_url = (
2559 'ldap:///_?mail,displayName?sub?'
2560 '(&(|(objectClass=inetLocalMailRecipient)(objectClass=aeContact))(mail=*)(aeStatus=0))'
2561 )
2562 html_tmpl = RFC822Address.html_tmpl
2563 show_val_button = False
2564
2565 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
2566 if 'member' not in self._entry:
2567 return []
2569 return []
2570 entrydn_filter = compose_filter(
2571 '|',
2572 map_filter_parts(
2573 'entryDN',
2574 decode_list(self._entry['member'], encoding=self._app.ls.charset),
2575 ),
2576 )
2577 ldap_result = self._app.ls.l.search_s(
2578 self._search_root(),
2579 ldap0.SCOPE_SUBTREE,
2580 entrydn_filter,
2581 attrlist=['mail'],
2582 )
2583 mail_addresses = []
2584 for res in ldap_result or []:
2585 mail_addresses.extend(res.entry_as['mail'])
2586 return sorted(mail_addresses)
2587
2588 def input_field(self) -> Field:
2589 input_field = HiddenInput(
2590 self._at,
2591 ': '.join([self._at, self.desc]),
2592 self.max_len, self.max_values, None,
2593 )
2594 input_field.charset = self._app.form.accept_charset
2595 input_field.set_default(self.form_value())
2596 return input_field
2597
2598syntax_registry.reg_at(
2599 AERFC822MailMember.oid, [
2600 '1.3.6.1.4.1.42.2.27.2.1.15', # rfc822MailMember
2601 ],
2602 structural_oc_oids=[
2603 AE_MAILGROUP_OID, # aeMailGroup
2604 ]
2605)
2606
2607
2609 """
2610 Plugin for attribute 'pwdPolicySubentry' in aeUser, aeService and aeHost entries
2611 """
2612 oid: str = 'AEPwdPolicy-oid'
2613 desc: str = 'AE-DIR: pwdPolicySubentry'
2614 ldap_url = 'ldap:///_??sub?(&(objectClass=aePolicy)(objectClass=pwdPolicy)(aeStatus=0))'
2615
2616syntax_registry.reg_at(
2617 AEPwdPolicy.oid, [
2618 '1.3.6.1.4.1.42.2.27.8.1.23', # pwdPolicySubentry
2619 ],
2620 structural_oc_oids=[
2621 AE_USER_OID, # aeUser
2622 AE_SERVICE_OID, # aeService
2623 AE_HOST_OID, # aeHost
2624 ]
2625)
2626
2627
2629 """
2630 Plugin for attribute 'sudoHost' in aeSudoRule entries
2631 """
2632 oid: str = 'AESudoHost-oid'
2633 desc: str = 'AE-DIR: sudoHost'
2634 max_values = 1
2635
2636 def transmute(self, attr_values: List[bytes]) -> List[bytes]:
2637 return [b'ALL']
2638
2639 def input_field(self) -> Field:
2640 input_field = HiddenInput(
2641 self._at,
2642 ': '.join([self._at, self.desc]),
2643 self.max_len, self.max_values, None,
2644 default=self.form_value()
2645 )
2646 input_field.charset = self._app.form.accept_charset
2647 return input_field
2648
2649syntax_registry.reg_at(
2650 AESudoHost.oid, [
2651 '1.3.6.1.4.1.15953.9.1.2', # sudoHost
2652 ],
2653 structural_oc_oids=[
2654 AE_SUDORULE_OID, # aeSudoRule
2655 ]
2656)
2657
2658
2660 """
2661 Plugin for attribute 'loginShell' in aeUser and aeService entries
2662 """
2663 oid: str = 'AELoginShell-oid'
2664 desc: str = 'AE-DIR: Login shell for POSIX users'
2665 attr_value_dict: Dict[str, str] = {
2666 '/bin/bash': '/bin/bash',
2667 '/bin/true': '/bin/true',
2668 '/bin/false': '/bin/false',
2669 }
2670
2671syntax_registry.reg_at(
2672 AELoginShell.oid, [
2673 '1.3.6.1.1.1.1.4', # loginShell
2674 ],
2675 structural_oc_oids=[
2676 AE_USER_OID, # aeUser
2677 AE_SERVICE_OID, # aeService
2678 ]
2679)
2680
2681
2683 """
2684 Plugin for attribute 'oathHOTPToken' in aeUser entries
2685 """
2686 oid: str = 'AEOathHOTPToken-oid'
2687 desc: str = 'DN of the associated oathHOTPToken entry in aeUser entry'
2688 ref_attrs = (
2689 (None, 'Users', None, None),
2690 )
2691 input_fallback = False
2692
2693 def _filterstr(self):
2694 if 'aePerson' in self._entry:
2695 return '(&{0}(aeOwner={1}))'.format(
2696 OathHOTPToken._filterstr(self),
2697 escape_filter_str(
2698 self._entry['aePerson'][0].decode(self._app.form.accept_charset)
2699 ),
2700 )
2701 return OathHOTPToken._filterstr(self)
2702
2703syntax_registry.reg_at(
2704 AEOathHOTPToken.oid, [
2705 '1.3.6.1.4.1.5427.1.389.4226.4.9.1', # oathHOTPToken
2706 ],
2707 structural_oc_oids=[AE_USER_OID], # aeUser
2708)
2709
2710
2711# see sshd(AUTHORIZED_KEYS FILE FORMAT
2712# and the -O option in ssh-keygen(1)
2714 """
2715 Plugin for attribute 'aeSSHPermissions' in aeUser and aeService entries
2716 """
2717 oid: str = 'AESSHPermissions-oid'
2718 desc: str = 'AE-DIR: Status of object'
2719 attr_value_dict: Dict[str, str] = {
2720 'pty': 'PTY allocation',
2721 'X11-forwarding': 'X11 forwarding',
2722 'agent-forwarding': 'Key agent forwarding',
2723 'port-forwarding': 'Port forwarding',
2724 'user-rc': 'Execute ~/.ssh/rc',
2725 }
2726
2727syntax_registry.reg_at(
2728 AESSHPermissions.oid, [
2729 AE_OID_PREFIX+'.4.47', # aeSSHPermissions
2730 ]
2731)
2732
2733
2735 """
2736 Plugin for attribute 'aeRemoteHost' in aeHost entries
2737 """
2738 oid: str = 'AERemoteHostAEHost-oid'
2739 desc: str = 'AE-DIR: aeRemoteHost in aeHost entry'
2740 ldap_url = 'ldap:///.?ipHostNumber,aeFqdn?one?(&(objectClass=aeNwDevice)(aeStatus=0))'
2741 input_fallback = True # fallback to normal input field
2742
2743syntax_registry.reg_at(
2744 AERemoteHostAEHost.oid, [
2745 AE_OID_PREFIX+'.4.8', # aeRemoteHost
2746 ],
2747 structural_oc_oids=[AE_HOST_OID], # aeHost
2748)
2749
2750
2752 """
2753 Plugin for attribute 'description' in aeNwDevice entries
2754 """
2755 oid: str = 'AEDescriptionAENwDevice-oid'
2756 desc: str = 'Attribute description in object class aeNwDevice'
2757 compose_templates = (
2758 '{cn}: {aeFqdn} / {ipHostNumber}',
2759 '{cn}: {ipHostNumber}',
2760 )
2761
2762syntax_registry.reg_at(
2763 AEDescriptionAENwDevice.oid, [
2764 '2.5.4.13', # description
2765 ],
2766 structural_oc_oids=[AE_NWDEVICE_OID], # aeNwDevice
2767)
2768
2769
2771 """
2772 Plugin for attribute 'aeChildClasses' in aeZone entries
2773 """
2774 oid = 'AEChildClasses-oid'
2775 desc = 'AE-DIR: Structural object classes allowed to be added in child entries'
2776 attr_value_dict: Dict[str, str] = {
2777 '-/-': '',
2778 'aeAuthcToken': 'Authentication Token (aeAuthcToken)',
2779 'aeContact': 'Contact (aeContact)',
2780 'aeDept': 'Department (aeDept)',
2781 'aeLocation': 'Location (aeLocation)',
2782 'aeMailGroup': 'Mail Group (aeMailGroup)',
2783 'aePerson': 'Person (aePerson)',
2784 'aePolicy': 'Policy (aePolicy)',
2785 'aeService': 'Service/tool Account (aeService)',
2786 'aeSrvGroup': 'Service Group (aeSrvGroup)',
2787 'aeSudoRule': 'Sudoers Rule (sudoRole)',
2788 'aeUser': 'User account (aeUser)',
2789 'aeGroup': 'User group (aeGroup)',
2790 'aeTag': 'Tag (aeTag)',
2791 }
2792
2793syntax_registry.reg_at(
2794 AEChildClasses.oid, [
2795 AE_OID_PREFIX+'.4.49', # aeChildClasses
2796 ]
2797)
2798
2799
2800# Register all syntax classes in this module
2801syntax_registry.reg_syntaxes(__name__)
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:2087
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:1614
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:261
str display(self, vidx, links)
Definition: aedir.py:730
def _extract_attr_value_dict(self, ldap_result, deref_person_attrset)
Definition: aedir.py:557
Dict[str, str] get_attr_value_dict(self)
Definition: aedir.py:593
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:627
bool _validate(self, bytes attr_value)
Definition: aedir.py:621
bool _validate(self, bytes attr_value)
Definition: aedir.py:153
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:163
bool _validate(self, bytes attr_value)
Definition: aedir.py:1832
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:1849
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:686
str display(self, vidx, links)
Definition: aedir.py:706
bool _validate(self, bytes attr_value)
Definition: aedir.py:679
bool _validate(self, bytes attr_value)
Definition: aedir.py:2278
def _zone_entry(self, attrlist=None)
Definition: aedir.py:114
bool _validate(self, bytes attr_value)
Definition: aedir.py:1769
bool _validate(self, bytes attr_value)
Definition: aedir.py:1553
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:2565
bool _validate(self, bytes attr_value)
Definition: aedir.py:2321
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:2350
str display(self, vidx, links)
Definition: aedir.py:2384
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:2636
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:208
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:1984
Dict[str, str] get_attr_value_dict(self)
Definition: aedir.py:1703
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:1728
bool _validate(self, bytes attr_value)
Definition: aedir.py:1715
def __init__(self, app, str dn, schema, str attrType, bytes attr_value, entry=None)
Definition: aedir.py:330
bytes sanitize(self, bytes attr_value)
Definition: aedir.py:379
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:889
bytes sanitize(self, bytes attr_value)
Definition: aedir.py:2117
bool _validate(self, bytes attr_value)
Definition: aedir.py:2124
List[bytes] transmute(self, List[bytes] attr_values)
Definition: aedir.py:2120
Dict[str, str] get_attr_value_dict(self)
Definition: syntaxes.py:1816
name
Definition: setup.py:37
def ae_validity_filter(secs=None)
Definition: aedir.py:90