Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

models.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. """
  2. schema.py - support for subSchemaSubEntry information
  3. See https://www.python-ldap.org/ for details.
  4. """
  5. import sys
  6. import ldap.cidict
  7. from ldap.compat import IterableUserDict
  8. from ldap.schema.tokenizer import split_tokens,extract_tokens
  9. NOT_HUMAN_READABLE_LDAP_SYNTAXES = {
  10. '1.3.6.1.4.1.1466.115.121.1.4', # Audio
  11. '1.3.6.1.4.1.1466.115.121.1.5', # Binary
  12. '1.3.6.1.4.1.1466.115.121.1.8', # Certificate
  13. '1.3.6.1.4.1.1466.115.121.1.9', # Certificate List
  14. '1.3.6.1.4.1.1466.115.121.1.10', # Certificate Pair
  15. '1.3.6.1.4.1.1466.115.121.1.23', # G3 FAX
  16. '1.3.6.1.4.1.1466.115.121.1.28', # JPEG
  17. '1.3.6.1.4.1.1466.115.121.1.40', # Octet String
  18. '1.3.6.1.4.1.1466.115.121.1.49', # Supported Algorithm
  19. }
  20. class SchemaElement:
  21. """
  22. Base class for all schema element classes. Not used directly!
  23. Arguments:
  24. schema_element_str
  25. String which contains the schema element description to be parsed.
  26. (Bytestrings are decoded using UTF-8)
  27. Class attributes:
  28. schema_attribute
  29. LDAP attribute type containing a certain schema element description
  30. token_defaults
  31. Dictionary internally used by the schema element parser
  32. containing the defaults for certain schema description key-words
  33. """
  34. token_defaults = {
  35. 'DESC':(None,),
  36. }
  37. def __init__(self,schema_element_str=None):
  38. if sys.version_info >= (3, 0) and isinstance(schema_element_str, bytes):
  39. schema_element_str = schema_element_str.decode('utf-8')
  40. if schema_element_str:
  41. l = split_tokens(schema_element_str)
  42. self.set_id(l[1])
  43. d = extract_tokens(l,self.token_defaults)
  44. self._set_attrs(l,d)
  45. def _set_attrs(self,l,d):
  46. self.desc = d['DESC'][0]
  47. return
  48. def set_id(self,element_id):
  49. self.oid = element_id
  50. def get_id(self):
  51. return self.oid
  52. def key_attr(self,key,value,quoted=0):
  53. assert value is None or type(value)==str,TypeError("value has to be of str, was %r" % value)
  54. if value:
  55. if quoted:
  56. return " %s '%s'" % (key,value.replace("'","\\'"))
  57. else:
  58. return " %s %s" % (key,value)
  59. else:
  60. return ""
  61. def key_list(self,key,values,sep=' ',quoted=0):
  62. assert type(values)==tuple,TypeError("values has to be a tuple, was %r" % values)
  63. if not values:
  64. return ''
  65. if quoted:
  66. quoted_values = [ "'%s'" % value.replace("'","\\'") for value in values ]
  67. else:
  68. quoted_values = values
  69. if len(values)==1:
  70. return ' %s %s' % (key,quoted_values[0])
  71. else:
  72. return ' %s ( %s )' % (key,sep.join(quoted_values))
  73. def __str__(self):
  74. result = [str(self.oid)]
  75. result.append(self.key_attr('DESC',self.desc,quoted=1))
  76. return '( %s )' % ''.join(result)
  77. class ObjectClass(SchemaElement):
  78. """
  79. Arguments:
  80. schema_element_str
  81. String containing an ObjectClassDescription
  82. Class attributes:
  83. oid
  84. OID assigned to the object class
  85. names
  86. This list of strings contains all NAMEs of the object class
  87. desc
  88. This string contains description text (DESC) of the object class
  89. obsolete
  90. Integer flag (0 or 1) indicating whether the object class is marked
  91. as OBSOLETE in the schema
  92. must
  93. This list of strings contains NAMEs or OIDs of all attributes
  94. an entry of the object class must have
  95. may
  96. This list of strings contains NAMEs or OIDs of additional attributes
  97. an entry of the object class may have
  98. kind
  99. Kind of an object class:
  100. 0 = STRUCTURAL,
  101. 1 = ABSTRACT,
  102. 2 = AUXILIARY
  103. sup
  104. This list of strings contains NAMEs or OIDs of object classes
  105. this object class is derived from
  106. """
  107. schema_attribute = u'objectClasses'
  108. token_defaults = {
  109. 'NAME':(()),
  110. 'DESC':(None,),
  111. 'OBSOLETE':None,
  112. 'SUP':(()),
  113. 'STRUCTURAL':None,
  114. 'AUXILIARY':None,
  115. 'ABSTRACT':None,
  116. 'MUST':(()),
  117. 'MAY':()
  118. }
  119. def _set_attrs(self,l,d):
  120. self.obsolete = d['OBSOLETE']!=None
  121. self.names = d['NAME']
  122. self.desc = d['DESC'][0]
  123. self.must = d['MUST']
  124. self.may = d['MAY']
  125. # Default is STRUCTURAL, see RFC2552 or draft-ietf-ldapbis-syntaxes
  126. self.kind = 0
  127. if d['ABSTRACT']!=None:
  128. self.kind = 1
  129. elif d['AUXILIARY']!=None:
  130. self.kind = 2
  131. if self.kind==0 and not d['SUP'] and self.oid!='2.5.6.0':
  132. # STRUCTURAL object classes are sub-classes of 'top' by default
  133. self.sup = ('top',)
  134. else:
  135. self.sup = d['SUP']
  136. return
  137. def __str__(self):
  138. result = [str(self.oid)]
  139. result.append(self.key_list('NAME',self.names,quoted=1))
  140. result.append(self.key_attr('DESC',self.desc,quoted=1))
  141. result.append(self.key_list('SUP',self.sup,sep=' $ '))
  142. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  143. result.append({0:' STRUCTURAL',1:' ABSTRACT',2:' AUXILIARY'}[self.kind])
  144. result.append(self.key_list('MUST',self.must,sep=' $ '))
  145. result.append(self.key_list('MAY',self.may,sep=' $ '))
  146. return '( %s )' % ''.join(result)
  147. AttributeUsage = ldap.cidict.cidict({
  148. 'userApplication':0, # work-around for non-compliant schema
  149. 'userApplications':0,
  150. 'directoryOperation':1,
  151. 'distributedOperation':2,
  152. 'dSAOperation':3,
  153. })
  154. class AttributeType(SchemaElement):
  155. """
  156. Arguments:
  157. schema_element_str
  158. String containing an AttributeTypeDescription
  159. Class attributes:
  160. oid
  161. OID assigned to the attribute type
  162. names
  163. This list of strings contains all NAMEs of the attribute type
  164. desc
  165. This string contains description text (DESC) of the attribute type
  166. obsolete
  167. Integer flag (0 or 1) indicating whether the attribute type is marked
  168. as OBSOLETE in the schema
  169. single_value
  170. Integer flag (0 or 1) indicating whether the attribute must
  171. have only one value
  172. syntax
  173. String contains OID of the LDAP syntax assigned to the attribute type
  174. no_user_mod
  175. Integer flag (0 or 1) indicating whether the attribute is modifiable
  176. by a client application
  177. equality
  178. String contains NAME or OID of the matching rule used for
  179. checking whether attribute values are equal
  180. substr
  181. String contains NAME or OID of the matching rule used for
  182. checking whether an attribute value contains another value
  183. ordering
  184. String contains NAME or OID of the matching rule used for
  185. checking whether attribute values are lesser-equal than
  186. usage
  187. USAGE of an attribute type:
  188. 0 = userApplications
  189. 1 = directoryOperation,
  190. 2 = distributedOperation,
  191. 3 = dSAOperation
  192. sup
  193. This list of strings contains NAMEs or OIDs of attribute types
  194. this attribute type is derived from
  195. """
  196. schema_attribute = u'attributeTypes'
  197. token_defaults = {
  198. 'NAME':(()),
  199. 'DESC':(None,),
  200. 'OBSOLETE':None,
  201. 'SUP':(()),
  202. 'EQUALITY':(None,),
  203. 'ORDERING':(None,),
  204. 'SUBSTR':(None,),
  205. 'SYNTAX':(None,),
  206. 'SINGLE-VALUE':None,
  207. 'COLLECTIVE':None,
  208. 'NO-USER-MODIFICATION':None,
  209. 'USAGE':('userApplications',),
  210. 'X-ORIGIN':(None,),
  211. 'X-ORDERED':(None,),
  212. }
  213. def _set_attrs(self,l,d):
  214. self.names = d['NAME']
  215. self.desc = d['DESC'][0]
  216. self.obsolete = d['OBSOLETE']!=None
  217. self.sup = d['SUP']
  218. self.equality = d['EQUALITY'][0]
  219. self.ordering = d['ORDERING'][0]
  220. self.substr = d['SUBSTR'][0]
  221. self.x_origin = d['X-ORIGIN'][0]
  222. self.x_ordered = d['X-ORDERED'][0]
  223. try:
  224. syntax = d['SYNTAX'][0]
  225. except IndexError:
  226. self.syntax = None
  227. self.syntax_len = None
  228. else:
  229. if syntax is None:
  230. self.syntax = None
  231. self.syntax_len = None
  232. else:
  233. try:
  234. self.syntax,syntax_len = d['SYNTAX'][0].split("{")
  235. except ValueError:
  236. self.syntax = d['SYNTAX'][0]
  237. self.syntax_len = None
  238. for i in l:
  239. if i.startswith("{") and i.endswith("}"):
  240. self.syntax_len = int(i[1:-1])
  241. else:
  242. self.syntax_len = int(syntax_len[:-1])
  243. self.single_value = d['SINGLE-VALUE']!=None
  244. self.collective = d['COLLECTIVE']!=None
  245. self.no_user_mod = d['NO-USER-MODIFICATION']!=None
  246. self.usage = AttributeUsage.get(d['USAGE'][0],0)
  247. return
  248. def __str__(self):
  249. result = [str(self.oid)]
  250. result.append(self.key_list('NAME',self.names,quoted=1))
  251. result.append(self.key_attr('DESC',self.desc,quoted=1))
  252. result.append(self.key_list('SUP',self.sup,sep=' $ '))
  253. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  254. result.append(self.key_attr('EQUALITY',self.equality))
  255. result.append(self.key_attr('ORDERING',self.ordering))
  256. result.append(self.key_attr('SUBSTR',self.substr))
  257. result.append(self.key_attr('SYNTAX',self.syntax))
  258. if self.syntax_len!=None:
  259. result.append(('{%d}' % (self.syntax_len))*(self.syntax_len>0))
  260. result.append({0:'',1:' SINGLE-VALUE'}[self.single_value])
  261. result.append({0:'',1:' COLLECTIVE'}[self.collective])
  262. result.append({0:'',1:' NO-USER-MODIFICATION'}[self.no_user_mod])
  263. result.append(
  264. {
  265. 0:"",
  266. 1:" USAGE directoryOperation",
  267. 2:" USAGE distributedOperation",
  268. 3:" USAGE dSAOperation",
  269. }[self.usage]
  270. )
  271. result.append(self.key_attr('X-ORIGIN',self.x_origin,quoted=1))
  272. result.append(self.key_attr('X-ORDERED',self.x_ordered,quoted=1))
  273. return '( %s )' % ''.join(result)
  274. class LDAPSyntax(SchemaElement):
  275. """
  276. SyntaxDescription
  277. oid
  278. OID assigned to the LDAP syntax
  279. desc
  280. This string contains description text (DESC) of the LDAP syntax
  281. not_human_readable
  282. Integer flag (0 or 1) indicating whether the attribute type is marked
  283. as not human-readable (X-NOT-HUMAN-READABLE)
  284. """
  285. schema_attribute = u'ldapSyntaxes'
  286. token_defaults = {
  287. 'DESC':(None,),
  288. 'X-NOT-HUMAN-READABLE':(None,),
  289. 'X-BINARY-TRANSFER-REQUIRED':(None,),
  290. 'X-SUBST':(None,),
  291. }
  292. def _set_attrs(self,l,d):
  293. self.desc = d['DESC'][0]
  294. self.x_subst = d['X-SUBST'][0]
  295. self.not_human_readable = \
  296. self.oid in NOT_HUMAN_READABLE_LDAP_SYNTAXES or \
  297. d['X-NOT-HUMAN-READABLE'][0]=='TRUE'
  298. self.x_binary_transfer_required = d['X-BINARY-TRANSFER-REQUIRED'][0]=='TRUE'
  299. return
  300. def __str__(self):
  301. result = [str(self.oid)]
  302. result.append(self.key_attr('DESC',self.desc,quoted=1))
  303. result.append(self.key_attr('X-SUBST',self.x_subst,quoted=1))
  304. result.append(
  305. {0:'',1:" X-NOT-HUMAN-READABLE 'TRUE'"}[self.not_human_readable]
  306. )
  307. return '( %s )' % ''.join(result)
  308. class MatchingRule(SchemaElement):
  309. """
  310. Arguments:
  311. schema_element_str
  312. String containing an MatchingRuleDescription
  313. Class attributes:
  314. oid
  315. OID assigned to the matching rule
  316. names
  317. This list of strings contains all NAMEs of the matching rule
  318. desc
  319. This string contains description text (DESC) of the matching rule
  320. obsolete
  321. Integer flag (0 or 1) indicating whether the matching rule is marked
  322. as OBSOLETE in the schema
  323. syntax
  324. String contains OID of the LDAP syntax this matching rule is usable with
  325. """
  326. schema_attribute = u'matchingRules'
  327. token_defaults = {
  328. 'NAME':(()),
  329. 'DESC':(None,),
  330. 'OBSOLETE':None,
  331. 'SYNTAX':(None,),
  332. }
  333. def _set_attrs(self,l,d):
  334. self.names = d['NAME']
  335. self.desc = d['DESC'][0]
  336. self.obsolete = d['OBSOLETE']!=None
  337. self.syntax = d['SYNTAX'][0]
  338. return
  339. def __str__(self):
  340. result = [str(self.oid)]
  341. result.append(self.key_list('NAME',self.names,quoted=1))
  342. result.append(self.key_attr('DESC',self.desc,quoted=1))
  343. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  344. result.append(self.key_attr('SYNTAX',self.syntax))
  345. return '( %s )' % ''.join(result)
  346. class MatchingRuleUse(SchemaElement):
  347. """
  348. Arguments:
  349. schema_element_str
  350. String containing an MatchingRuleUseDescription
  351. Class attributes:
  352. oid
  353. OID of the accompanying matching rule
  354. names
  355. This list of strings contains all NAMEs of the matching rule
  356. desc
  357. This string contains description text (DESC) of the matching rule
  358. obsolete
  359. Integer flag (0 or 1) indicating whether the matching rule is marked
  360. as OBSOLETE in the schema
  361. applies
  362. This list of strings contains NAMEs or OIDs of attribute types
  363. for which this matching rule is used
  364. """
  365. schema_attribute = u'matchingRuleUse'
  366. token_defaults = {
  367. 'NAME':(()),
  368. 'DESC':(None,),
  369. 'OBSOLETE':None,
  370. 'APPLIES':(()),
  371. }
  372. def _set_attrs(self,l,d):
  373. self.names = d['NAME']
  374. self.desc = d['DESC'][0]
  375. self.obsolete = d['OBSOLETE']!=None
  376. self.applies = d['APPLIES']
  377. return
  378. def __str__(self):
  379. result = [str(self.oid)]
  380. result.append(self.key_list('NAME',self.names,quoted=1))
  381. result.append(self.key_attr('DESC',self.desc,quoted=1))
  382. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  383. result.append(self.key_list('APPLIES',self.applies,sep=' $ '))
  384. return '( %s )' % ''.join(result)
  385. class DITContentRule(SchemaElement):
  386. """
  387. Arguments:
  388. schema_element_str
  389. String containing an DITContentRuleDescription
  390. Class attributes:
  391. oid
  392. OID of the accompanying structural object class
  393. names
  394. This list of strings contains all NAMEs of the DIT content rule
  395. desc
  396. This string contains description text (DESC) of the DIT content rule
  397. obsolete
  398. Integer flag (0 or 1) indicating whether the DIT content rule is marked
  399. as OBSOLETE in the schema
  400. aux
  401. This list of strings contains NAMEs or OIDs of all auxiliary
  402. object classes usable in an entry of the object class
  403. must
  404. This list of strings contains NAMEs or OIDs of all attributes
  405. an entry of the object class must have which may extend the
  406. list of required attributes of the object classes of an entry
  407. may
  408. This list of strings contains NAMEs or OIDs of additional attributes
  409. an entry of the object class may have which may extend the
  410. list of optional attributes of the object classes of an entry
  411. nots
  412. This list of strings contains NAMEs or OIDs of attributes which
  413. may not be present in an entry of the object class
  414. """
  415. schema_attribute = u'dITContentRules'
  416. token_defaults = {
  417. 'NAME':(()),
  418. 'DESC':(None,),
  419. 'OBSOLETE':None,
  420. 'AUX':(()),
  421. 'MUST':(()),
  422. 'MAY':(()),
  423. 'NOT':(()),
  424. }
  425. def _set_attrs(self,l,d):
  426. self.names = d['NAME']
  427. self.desc = d['DESC'][0]
  428. self.obsolete = d['OBSOLETE']!=None
  429. self.aux = d['AUX']
  430. self.must = d['MUST']
  431. self.may = d['MAY']
  432. self.nots = d['NOT']
  433. return
  434. def __str__(self):
  435. result = [str(self.oid)]
  436. result.append(self.key_list('NAME',self.names,quoted=1))
  437. result.append(self.key_attr('DESC',self.desc,quoted=1))
  438. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  439. result.append(self.key_list('AUX',self.aux,sep=' $ '))
  440. result.append(self.key_list('MUST',self.must,sep=' $ '))
  441. result.append(self.key_list('MAY',self.may,sep=' $ '))
  442. result.append(self.key_list('NOT',self.nots,sep=' $ '))
  443. return '( %s )' % ''.join(result)
  444. class DITStructureRule(SchemaElement):
  445. """
  446. Arguments:
  447. schema_element_str
  448. String containing an DITStructureRuleDescription
  449. Class attributes:
  450. ruleid
  451. rule ID of the DIT structure rule (only locally unique)
  452. names
  453. This list of strings contains all NAMEs of the DIT structure rule
  454. desc
  455. This string contains description text (DESC) of the DIT structure rule
  456. obsolete
  457. Integer flag (0 or 1) indicating whether the DIT content rule is marked
  458. as OBSOLETE in the schema
  459. form
  460. List of strings with NAMEs or OIDs of associated name forms
  461. sup
  462. List of strings with NAMEs or OIDs of allowed structural object classes
  463. of superior entries in the DIT
  464. """
  465. schema_attribute = u'dITStructureRules'
  466. token_defaults = {
  467. 'NAME':(()),
  468. 'DESC':(None,),
  469. 'OBSOLETE':None,
  470. 'FORM':(None,),
  471. 'SUP':(()),
  472. }
  473. def set_id(self,element_id):
  474. self.ruleid = element_id
  475. def get_id(self):
  476. return self.ruleid
  477. def _set_attrs(self,l,d):
  478. self.names = d['NAME']
  479. self.desc = d['DESC'][0]
  480. self.obsolete = d['OBSOLETE']!=None
  481. self.form = d['FORM'][0]
  482. self.sup = d['SUP']
  483. return
  484. def __str__(self):
  485. result = [str(self.ruleid)]
  486. result.append(self.key_list('NAME',self.names,quoted=1))
  487. result.append(self.key_attr('DESC',self.desc,quoted=1))
  488. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  489. result.append(self.key_attr('FORM',self.form,quoted=0))
  490. result.append(self.key_list('SUP',self.sup,sep=' $ '))
  491. return '( %s )' % ''.join(result)
  492. class NameForm(SchemaElement):
  493. """
  494. Arguments:
  495. schema_element_str
  496. String containing an NameFormDescription
  497. Class attributes:
  498. oid
  499. OID of the name form
  500. names
  501. This list of strings contains all NAMEs of the name form
  502. desc
  503. This string contains description text (DESC) of the name form
  504. obsolete
  505. Integer flag (0 or 1) indicating whether the name form is marked
  506. as OBSOLETE in the schema
  507. form
  508. List of strings with NAMEs or OIDs of associated name forms
  509. oc
  510. String with NAME or OID of structural object classes this name form
  511. is usable with
  512. must
  513. This list of strings contains NAMEs or OIDs of all attributes
  514. an RDN must contain
  515. may
  516. This list of strings contains NAMEs or OIDs of additional attributes
  517. an RDN may contain
  518. """
  519. schema_attribute = u'nameForms'
  520. token_defaults = {
  521. 'NAME':(()),
  522. 'DESC':(None,),
  523. 'OBSOLETE':None,
  524. 'OC':(None,),
  525. 'MUST':(()),
  526. 'MAY':(()),
  527. }
  528. def _set_attrs(self,l,d):
  529. self.names = d['NAME']
  530. self.desc = d['DESC'][0]
  531. self.obsolete = d['OBSOLETE']!=None
  532. self.oc = d['OC'][0]
  533. self.must = d['MUST']
  534. self.may = d['MAY']
  535. return
  536. def __str__(self):
  537. result = [str(self.oid)]
  538. result.append(self.key_list('NAME',self.names,quoted=1))
  539. result.append(self.key_attr('DESC',self.desc,quoted=1))
  540. result.append({0:'',1:' OBSOLETE'}[self.obsolete])
  541. result.append(self.key_attr('OC',self.oc))
  542. result.append(self.key_list('MUST',self.must,sep=' $ '))
  543. result.append(self.key_list('MAY',self.may,sep=' $ '))
  544. return '( %s )' % ''.join(result)
  545. class Entry(IterableUserDict):
  546. """
  547. Schema-aware implementation of an LDAP entry class.
  548. Mainly it holds the attributes in a string-keyed dictionary with
  549. the OID as key.
  550. """
  551. def __init__(self,schema,dn,entry):
  552. self._keytuple2attrtype = {}
  553. self._attrtype2keytuple = {}
  554. self._s = schema
  555. self.dn = dn
  556. IterableUserDict.IterableUserDict.__init__(self,{})
  557. self.update(entry)
  558. def _at2key(self,nameoroid):
  559. """
  560. Return tuple of OID and all sub-types of attribute type specified
  561. in nameoroid.
  562. """
  563. try:
  564. # Mapping already in cache
  565. return self._attrtype2keytuple[nameoroid]
  566. except KeyError:
  567. # Mapping has to be constructed
  568. oid = self._s.getoid(ldap.schema.AttributeType,nameoroid)
  569. l = nameoroid.lower().split(';')
  570. l[0] = oid
  571. t = tuple(l)
  572. self._attrtype2keytuple[nameoroid] = t
  573. return t
  574. def update(self,dict):
  575. for key, value in dict.values():
  576. self[key] = value
  577. def __contains__(self,nameoroid):
  578. return self._at2key(nameoroid) in self.data
  579. def __getitem__(self,nameoroid):
  580. return self.data[self._at2key(nameoroid)]
  581. def __setitem__(self,nameoroid,attr_values):
  582. k = self._at2key(nameoroid)
  583. self._keytuple2attrtype[k] = nameoroid
  584. self.data[k] = attr_values
  585. def __delitem__(self,nameoroid):
  586. k = self._at2key(nameoroid)
  587. del self.data[k]
  588. del self._attrtype2keytuple[nameoroid]
  589. del self._keytuple2attrtype[k]
  590. def has_key(self,nameoroid):
  591. k = self._at2key(nameoroid)
  592. return k in self.data
  593. def keys(self):
  594. return self._keytuple2attrtype.values()
  595. def items(self):
  596. return [
  597. (k,self[k])
  598. for k in self.keys()
  599. ]
  600. def attribute_types(
  601. self,attr_type_filter=None,raise_keyerror=1
  602. ):
  603. """
  604. Convenience wrapper around SubSchema.attribute_types() which
  605. passes object classes of this particular entry as argument to
  606. SubSchema.attribute_types()
  607. """
  608. return self._s.attribute_types(
  609. self.get('objectClass',[]),attr_type_filter,raise_keyerror
  610. )