Package ldaptor :: Module schema
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.schema

1 -def extractWord(text):
2 if not text: 3 return None 4 l = text.split(None, 1) 5 word = l[0] 6 try: 7 text = l[1] 8 except IndexError: 9 text = '' 10 return word, text
11
12 -def peekWord(text):
13 if not text: 14 return None 15 return text.split(None, 1)[0]
16
17 -class ASN1ParserThingie:
18 - def _to_list(self, text):
19 """Split text into $-separated list.""" 20 r=[] 21 for x in text.split("$"): 22 x = x.strip() 23 assert x 24 r.append(x) 25 return tuple(r)
26
27 - def _strings_to_list(self, text):
28 """Split ''-quoted strings into list.""" 29 r=[] 30 while text: 31 text = text.lstrip() 32 if not text: 33 break 34 assert text[0]=="'", "Text %s must start with a single quote."%repr(text) 35 text=text[1:] 36 end=text.index("'") 37 r.append(text[:end]) 38 text=text[end+1:] 39 return tuple(r)
40
41 - def _str_list(self, l):
42 s = ' '.join([self._str(x) for x in l]) 43 if len(l) > 1: 44 s = '( %s )' % s 45 return s
46
47 - def _list(self, l):
48 s = ' $ '.join([x for x in l]) 49 if len(l) > 1: 50 s = '( %s )' % s 51 return s
52
53 - def _str(self, s):
54 return "'%s'" % s
55
56 -class ObjectClassDescription(ASN1ParserThingie):
57 """ 58 ASN Syntax:: 59 60 d = "0" / "1" / "2" / "3" / "4" / 61 "5" / "6" / "7" / "8" / "9" 62 63 numericstring = 1*d 64 65 numericoid = numericstring *( "." numericstring ) 66 67 space = 1*" " 68 69 whsp = [ space ] 70 71 descr = keystring 72 73 qdescr = whsp "'" descr "'" whsp 74 75 qdescrlist = [ qdescr *( qdescr ) ] 76 77 ; object descriptors used as schema element names 78 qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp ) 79 80 dstring = 1*utf8 81 82 qdstring = whsp "'" dstring "'" whsp 83 84 descr = keystring 85 86 oid = descr / numericoid 87 88 woid = whsp oid whsp 89 90 ; set of oids of either form 91 oids = woid / ( "(" oidlist ")" ) 92 93 ObjectClassDescription = "(" whsp 94 numericoid whsp ; ObjectClass identifier 95 [ "NAME" qdescrs ] 96 [ "DESC" qdstring ] 97 [ "OBSOLETE" whsp ] 98 [ "SUP" oids ] ; Superior ObjectClasses 99 [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ] 100 ; default structural 101 [ "MUST" oids ] ; AttributeTypes 102 [ "MAY" oids ] ; AttributeTypes 103 whsp ")" 104 """ 105 106
107 - def __init__(self, text):
108 self.oid=None 109 self.name=None 110 self.desc=None 111 self.obsolete=0 112 self.sup=[] 113 self.type=None 114 self.must=[] 115 self.may=[] 116 117 if text is not None: 118 self._parse(text)
119
120 - def _parse(self, text):
121 assert text[0]=='(', "Text %s must be in parentheses."%repr(text) 122 assert text[-1]==')', "Text %s must be in parentheses."%repr(text) 123 text=text[1:-1] 124 text = text.lstrip() 125 126 # oid 127 self.oid, text = extractWord(text) 128 129 text = text.lstrip() 130 131 if peekWord(text) == "NAME": 132 text=text[len("NAME "):] 133 text = text.lstrip() 134 if text[0]=="'": 135 text=text[1:] 136 end=text.index("'") 137 self.name=(text[:end],) 138 text=text[end+1:] 139 elif text[0]=="(": 140 text=text[1:] 141 text = text.lstrip() 142 end=text.index(")") 143 self.name=self._strings_to_list(text[:end]) 144 text=text[end+1:] 145 else: 146 raise "TODO" 147 148 149 text = text.lstrip() 150 151 if peekWord(text) == "DESC": 152 text=text[len("DESC "):] 153 text = text.lstrip() 154 assert text[0]=="'" 155 text=text[1:] 156 end=text.index("'") 157 self.desc=text[:end] 158 text=text[end+1:] 159 160 text = text.lstrip() 161 162 if peekWord(text) == "OBSOLETE": 163 self.obsolete=1 164 text=text[len("OBSOLETE "):] 165 166 text = text.lstrip() 167 168 if peekWord(text) == "SUP": 169 text=text[len("SUP "):] 170 text = text.lstrip() 171 if text[0]=="(": 172 text=text[1:] 173 text = text.lstrip() 174 end=text.index(")") 175 self.sup=self._to_list(text[:end]) 176 text=text[end+1:] 177 else: 178 s, text = extractWord(text) 179 self.sup=[s] 180 181 text = text.lstrip() 182 183 if peekWord(text) == "ABSTRACT": 184 assert self.type is None 185 self.type="ABSTRACT" 186 text=text[len("ABSTRACT "):] 187 188 text = text.lstrip() 189 190 if peekWord(text) == "STRUCTURAL": 191 assert self.type is None 192 self.type="STRUCTURAL" 193 text=text[len("STRUCTURAL "):] 194 195 text = text.lstrip() 196 197 if peekWord(text) == "AUXILIARY": 198 assert self.type is None 199 self.type="AUXILIARY" 200 text=text[len("AUXILIARY "):] 201 202 text = text.lstrip() 203 204 if peekWord(text) == "MUST": 205 text=text[len("MUST "):] 206 text = text.lstrip() 207 if text[0]=="(": 208 text=text[1:] 209 text = text.lstrip() 210 end=text.index(")") 211 self.must.extend(self._to_list(text[:end])) 212 text=text[end+1:] 213 else: 214 s, text = extractWord(text) 215 self.must.append(s) 216 217 text = text.lstrip() 218 219 if peekWord(text) == "MAY": 220 text=text[len("MAY "):] 221 text = text.lstrip() 222 if text[0]=="(": 223 text=text[1:] 224 text = text.lstrip() 225 end=text.index(")") 226 self.may.extend(self._to_list(text[:end])) 227 text=text[end+1:] 228 else: 229 s, text = extractWord(text) 230 self.may.append(s) 231 232 text = text.lstrip() 233 234 assert text=="", "Text was not empty: %s"%repr(text) 235 236 if not self.type: 237 self.type="STRUCTURAL" 238 239 assert self.oid 240 for c in self.oid: 241 assert c in "0123456789." 242 assert self.name is None or self.name 243 assert self.type in ("ABSTRACT", "STRUCTURAL", "AUXILIARY")
244
245 - def __repr__(self):
246 nice = {} 247 for k,v in self.__dict__.items(): 248 nice[k]=repr(v) 249 return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) 250 +(" oid=%(oid)s name=%(name)s desc=%(desc)s" 251 +" obsolete=%(obsolete)s sup=%(sup)s type=%(type)s" 252 +" must=%(must)s may=%(may)s>")%nice)
253
254 - def __str__(self):
255 r=[] 256 if self.name is not None: 257 r.append('NAME %s' % self._str_list(self.name)) 258 if self.desc is not None: 259 r.append('DESC %s' % self._str(self.desc)) 260 if self.obsolete: 261 r.append('OBSOLETE') 262 if self.sup: 263 r.append('SUP %s' % self._list(self.sup)) 264 r.append('%s' % self.type) 265 if self.must: 266 r.append('MUST %s' % self._list(self.must)) 267 if self.may: 268 r.append('MAY %s' % self._list(self.may)) 269 return ('( %s ' % self.oid 270 + '\n '.join(r) 271 + ' )')
272
273 - def __lt__(self, other):
274 if not isinstance(other, ObjectClassDescription): 275 return NotImplemented 276 if self.name is not None and other.name is not None: 277 return self.name[0].upper() < other.name[0].upper() 278 else: 279 return self.oid < other.oid
280
281 - def __gt__(self, other):
282 if not isinstance(other, ObjectClassDescription): 283 return NotImplemented 284 if self.name is not None and other.name is not None: 285 return self.name[0].upper() > other.name[0].upper() 286 else: 287 return self.oid > other.oid
288
289 - def __le__(self, other):
290 return self == other or self < other
291
292 - def __ge__(self, other):
293 return self == other or self > other
294
295 - def __eq__(self, other):
296 if not isinstance(other, ObjectClassDescription): 297 return NotImplemented 298 return (self.oid == other.oid 299 and self.name == other.name 300 and self.desc == other.desc 301 and self.obsolete == other.obsolete 302 and self.sup == other.sup 303 and self.type == other.type 304 and self.must == other.must 305 and self.may == other.may)
306
307 - def __ne__(self, other):
308 return not (self == other)
309
310 -class AttributeTypeDescription(ASN1ParserThingie):
311 """ 312 ASN Syntax:: 313 314 AttributeTypeDescription = "(" whsp 315 numericoid whsp ; AttributeType identifier 316 [ "NAME" qdescrs ] ; name used in AttributeType 317 [ "DESC" qdstring ] ; description 318 [ "OBSOLETE" whsp ] 319 [ "SUP" woid ] ; derived from this other AttributeType 320 [ "EQUALITY" woid ; Matching Rule name 321 [ "ORDERING" woid ; Matching Rule name 322 [ "SUBSTR" woid ] ; Matching Rule name 323 [ "SYNTAX" whsp noidlen whsp ] ; see section 4.3 324 [ "SINGLE-VALUE" whsp ] ; default multi-valued 325 [ "COLLECTIVE" whsp ] ; default not collective 326 [ "NO-USER-MODIFICATION" whsp ]; default user modifiable 327 [ "USAGE" whsp AttributeUsage ]; default userApplications 328 whsp ")" 329 330 AttributeUsage = 331 "userApplications" / 332 "directoryOperation" / 333 "distributedOperation" / ; DSA-shared 334 "dSAOperation" ; DSA-specific, value depends on server 335 336 noidlen = numericoid [ "{" len "}" ] 337 338 len = numericstring 339 """ 340
341 - def __init__(self, text):
342 self.oid=None 343 self.name=None 344 self.desc=None 345 self.obsolete=0 346 self.sup=None 347 self.equality=None 348 self.ordering=None 349 self.substr=None 350 self.syntax=None 351 self.single_value=None 352 self.collective=None 353 self.no_user_modification=None 354 self.usage=None 355 356 # storage for experimental terms ("X-SOMETHING"), so we can 357 # output them when stringifying. 358 self.x_attrs=[] 359 360 if text is not None: 361 self._parse(text)
362
363 - def _parse(self, text):
364 assert text[0]=='(', "Text %s must be in parentheses."%repr(text) 365 assert text[-1]==')', "Text %s must be in parentheses."%repr(text) 366 text=text[1:-1] 367 text = text.lstrip() 368 369 # oid 370 self.oid, text = extractWord(text) 371 372 text = text.lstrip() 373 374 if peekWord(text) == "NAME": 375 text=text[len("NAME "):] 376 text = text.lstrip() 377 if text[0]=="'": 378 text=text[1:] 379 end=text.index("'") 380 self.name=(text[:end],) 381 text=text[end+1:] 382 elif text[0]=="(": 383 text=text[1:] 384 text = text.lstrip() 385 end=text.index(")") 386 self.name=self._strings_to_list(text[:end]) 387 text=text[end+1:] 388 else: 389 raise "TODO" 390 391 392 text = text.lstrip() 393 394 if peekWord(text) == "DESC": 395 text=text[len("DESC "):] 396 text = text.lstrip() 397 assert text[0]=="'" 398 text=text[1:] 399 end=text.index("'") 400 self.desc=text[:end] 401 text=text[end+1:] 402 403 text = text.lstrip() 404 405 if peekWord(text) == "OBSOLETE": 406 self.obsolete=1 407 text=text[len("OBSOLETE "):] 408 409 text = text.lstrip() 410 411 if peekWord(text) == "SUP": 412 text=text[len("SUP "):] 413 text = text.lstrip() 414 self.sup, text = extractWord(text) 415 416 text = text.lstrip() 417 418 if peekWord(text) == "EQUALITY": 419 text=text[len("EQUALITY "):] 420 text = text.lstrip() 421 self.equality, text = extractWord(text) 422 423 text = text.lstrip() 424 425 if peekWord(text) == "ORDERING": 426 text=text[len("ORDERING "):] 427 text = text.lstrip() 428 self.ordering, text = extractWord(text) 429 430 text = text.lstrip() 431 432 if peekWord(text) == "SUBSTR": 433 text=text[len("SUBSTR "):] 434 text = text.lstrip() 435 self.substr, text = extractWord(text) 436 437 text = text.lstrip() 438 439 if peekWord(text) == "SYNTAX": 440 text=text[len("SYNTAX "):] 441 text = text.lstrip() 442 self.syntax, text = extractWord(text) 443 444 text = text.lstrip() 445 446 if peekWord(text) == "SINGLE-VALUE": 447 assert self.single_value is None 448 self.single_value=1 449 text=text[len("SINGLE-VALUE "):] 450 451 text = text.lstrip() 452 453 if peekWord(text) == "COLLECTIVE": 454 assert self.collective is None 455 self.collective=1 456 text=text[len("COLLECTIVE "):] 457 458 text = text.lstrip() 459 460 if peekWord(text) == "NO-USER-MODIFICATION": 461 assert self.no_user_modification is None 462 self.no_user_modification=1 463 text=text[len("NO-USER-MODIFICATION "):] 464 465 text = text.lstrip() 466 467 if peekWord(text) == "USAGE": 468 assert self.usage is None 469 text=text[len("USAGE "):] 470 text = text.lstrip() 471 self.usage, text = extractWord(text) 472 473 474 while True: 475 text = text.lstrip() 476 477 word = peekWord(text) 478 if word is None: 479 break 480 481 if word.startswith('X-'): 482 text=text[len(word+" "):] 483 text = text.lstrip() 484 if text[0]=="'": 485 text=text[1:] 486 end=text.index("'") 487 value=text[:end] 488 text=text[end+1:] 489 elif text[0]=="(": 490 text=text[1:] 491 text = text.lstrip() 492 end=text.index(")") 493 value=self._strings_to_list(text[:end]) 494 text=text[end+1:] 495 else: 496 raise "TODO" 497 498 self.x_attrs.append((word, value)) 499 else: 500 raise RuntimeError('Unhandled attributeType: %r', word) 501 502 assert text=="", "Text was not empty: %s"%repr(text) 503 504 if self.single_value is None: 505 self.single_value=0 506 507 if self.collective is None: 508 self.collective=0 509 510 if self.no_user_modification is None: 511 self.no_user_modification=0 512 513 assert self.oid 514 for c in self.oid: 515 assert c in "0123456789." 516 assert self.name is None or self.name 517 assert self.usage is None or self.usage in ( 518 "userApplications", 519 "directoryOperation", 520 "distributedOperation", 521 "dSAOperation", 522 )
523
524 - def __repr__(self):
525 nice = {} 526 for k,v in self.__dict__.items(): 527 nice[k]=repr(v) 528 return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) 529 +(" oid=%(oid)s name=%(name)s desc=%(desc)s" 530 +" obsolete=%(obsolete)s sup=%(sup)s" 531 +" equality=%(equality)s ordering=%(ordering)s" 532 +" substr=%(substr)s syntax=%(syntax)s" 533 +" single_value=%(single_value)s" 534 +" collective=%(collective)s" 535 +" no_user_modification=%(no_user_modification)s" 536 +" usage=%(usage)s>")%nice)
537
538 - def __str__(self):
539 r=[] 540 if self.name is not None: 541 r.append('NAME %s' % self._str_list(self.name)) 542 if self.desc is not None: 543 r.append('DESC %s' % self._str(self.desc)) 544 if self.obsolete: 545 r.append('OBSOLETE') 546 if self.sup is not None: 547 r.append('SUP %s' % self.sup) 548 if self.equality is not None: 549 r.append('EQUALITY %s' % self.equality) 550 if self.ordering is not None: 551 r.append('ORDERING %s' % self.ordering) 552 if self.substr is not None: 553 r.append('SUBSTR %s' % self.substr) 554 if self.syntax is not None: 555 r.append('SYNTAX %s' % self.syntax) 556 if self.single_value: 557 r.append('SINGLE-VALUE') 558 if self.collective: 559 r.append('COLLECTIVE') 560 if self.no_user_modification: 561 r.append('NO-USER-MODIFICATION') 562 if self.usage is not None: 563 r.append('USAGE %s' % self.usage) 564 for name, value in self.x_attrs: 565 if isinstance(value, basestring): 566 r.append("%s '%s'" % (name, value)) 567 else: 568 r.append( 569 '%s ( %s )' % ( 570 name, 571 ' '.join("'%s'" % s for s in value), 572 ), 573 ) 574 return ('( %s ' % self.oid 575 + '\n '.join(r) 576 + ' )')
577
578 -class SyntaxDescription(ASN1ParserThingie):
579 """ 580 ASN Syntax:: 581 582 SyntaxDescription = "(" whsp 583 numericoid whsp 584 [ "DESC" qdstring ] 585 whsp ")" 586 """ 587
588 - def __init__(self, text):
589 self.oid=None 590 self.desc=None 591 592 assert text[0]=='(' 593 assert text[-1]==')' 594 text=text[1:-1] 595 text = text.lstrip() 596 597 # oid 598 self.oid, text = extractWord(text) 599 600 text = text.lstrip() 601 602 if peekWord(text) == "DESC": 603 text=text[len("DESC "):] 604 text = text.lstrip() 605 assert text[0]=="'" 606 text=text[1:] 607 end=text.index("'") 608 self.desc=text[:end] 609 text=text[end+1:] 610 611 text = text.lstrip() 612 613 if peekWord(text) == "X-BINARY-TRANSFER-REQUIRED": 614 text=text[len("X-BINARY-TRANSFER-REQUIRED "):] 615 text = text.lstrip() 616 assert text[0]=="'" 617 text=text[1:] 618 end=text.index("'") 619 self.desc=text[:end] 620 text=text[end+1:] 621 622 text = text.lstrip() 623 624 if peekWord(text) == "X-NOT-HUMAN-READABLE": 625 text=text[len("X-NOT-HUMAN-READABLE "):] 626 text = text.lstrip() 627 assert text[0]=="'" 628 text=text[1:] 629 end=text.index("'") 630 self.desc=text[:end] 631 text=text[end+1:] 632 633 text = text.lstrip() 634 635 assert text=="", "Text was not empty: %s"%repr(text) 636 637 assert self.oid 638 for c in self.oid: 639 assert c in "0123456789."
640
641 - def __repr__(self):
642 nice = {} 643 for k,v in self.__dict__.items(): 644 nice[k]=repr(v) 645 return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) 646 +(" oid=%(oid)s desc=%(desc)s>")%nice)
647 648 649
650 -class MatchingRuleDescription(ASN1ParserThingie):
651 """ 652 ASN Syntax:: 653 654 MatchingRuleDescription = "(" whsp 655 numericoid whsp ; MatchingRule identifier 656 [ "NAME" qdescrs ] 657 [ "DESC" qdstring ] 658 [ "OBSOLETE" whsp ] 659 "SYNTAX" numericoid 660 whsp ")" 661 """ 662
663 - def __init__(self, text):
664 self.oid=None 665 self.name=None 666 self.desc=None 667 self.obsolete=None 668 self.syntax=None 669 670 assert text[0]=='(' 671 assert text[-1]==')' 672 text=text[1:-1] 673 text = text.lstrip() 674 675 # oid 676 self.oid, text = extractWord(text) 677 678 text = text.lstrip() 679 680 if peekWord(text) == "NAME": 681 text=text[len("NAME "):] 682 text = text.lstrip() 683 if text[0]=="'": 684 text=text[1:] 685 end=text.index("'") 686 self.name=(text[:end],) 687 text=text[end+1:] 688 elif text[0]=="(": 689 text=text[1:] 690 text = text.lstrip() 691 end=text.index(")") 692 self.name=self._strings_to_list(text[:end]) 693 text=text[end+1:] 694 else: 695 raise "TODO" 696 697 text = text.lstrip() 698 699 if peekWord(text) == "DESC": 700 text=text[len("DESC "):] 701 text = text.lstrip() 702 assert text[0]=="'" 703 text=text[1:] 704 end=text.index("'") 705 self.desc=text[:end] 706 text=text[end+1:] 707 708 text = text.lstrip() 709 710 if peekWord(text) == "OBSOLETE": 711 self.obsolete=1 712 text=text[len("OBSOLETE "):] 713 714 text = text.lstrip() 715 716 if peekWord(text) == "SYNTAX": 717 text=text[len("SYNTAX "):] 718 text = text.lstrip() 719 self.syntax, text = extractWord(text) 720 721 text = text.lstrip() 722 723 assert text=="", "Text was not empty: %s"%repr(text) 724 725 if self.obsolete is None: 726 self.obsolete=0 727 assert self.oid 728 for c in self.oid: 729 assert c in "0123456789." 730 assert self.syntax
731
732 - def __repr__(self):
733 nice = {} 734 for k,v in self.__dict__.items(): 735 nice[k]=repr(v) 736 return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) 737 +(" oid=%(oid)s desc=%(desc)s>")%nice)
738