1# 2# This file is part of pyasn1 software. 3# 4# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com> 5# License: http://snmplabs.com/pyasn1/license.html 6# 7# Original concept and code by Mike C. Fletcher. 8# 9import sys 10 11from pyasn1.type import error 12 13__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', 14 'ValueRangeConstraint', 'ValueSizeConstraint', 15 'PermittedAlphabetConstraint', 'InnerTypeConstraint', 16 'ConstraintsExclusion', 'ConstraintsIntersection', 17 'ConstraintsUnion'] 18 19 20class AbstractConstraint(object): 21 22 def __init__(self, *values): 23 self._valueMap = set() 24 self._setValues(values) 25 self.__hash = hash((self.__class__.__name__, self._values)) 26 27 def __call__(self, value, idx=None): 28 if not self._values: 29 return 30 31 try: 32 self._testValue(value, idx) 33 34 except error.ValueConstraintError: 35 raise error.ValueConstraintError( 36 '%s failed at: %r' % (self, sys.exc_info()[1]) 37 ) 38 39 def __repr__(self): 40 representation = '%s object' % (self.__class__.__name__) 41 42 if self._values: 43 representation += ', consts %s' % ', '.join( 44 [repr(x) for x in self._values]) 45 46 return '<%s>' % representation 47 48 def __eq__(self, other): 49 return self is other and True or self._values == other 50 51 def __ne__(self, other): 52 return self._values != other 53 54 def __lt__(self, other): 55 return self._values < other 56 57 def __le__(self, other): 58 return self._values <= other 59 60 def __gt__(self, other): 61 return self._values > other 62 63 def __ge__(self, other): 64 return self._values >= other 65 66 if sys.version_info[0] <= 2: 67 def __nonzero__(self): 68 return self._values and True or False 69 else: 70 def __bool__(self): 71 return self._values and True or False 72 73 def __hash__(self): 74 return self.__hash 75 76 def _setValues(self, values): 77 self._values = values 78 79 def _testValue(self, value, idx): 80 raise error.ValueConstraintError(value) 81 82 # Constraints derivation logic 83 def getValueMap(self): 84 return self._valueMap 85 86 def isSuperTypeOf(self, otherConstraint): 87 # TODO: fix possible comparison of set vs scalars here 88 return (otherConstraint is self or 89 not self._values or 90 otherConstraint == self or 91 self in otherConstraint.getValueMap()) 92 93 def isSubTypeOf(self, otherConstraint): 94 return (otherConstraint is self or 95 not self or 96 otherConstraint == self or 97 otherConstraint in self._valueMap) 98 99 100class SingleValueConstraint(AbstractConstraint): 101 """Create a SingleValueConstraint object. 102 103 The SingleValueConstraint satisfies any value that 104 is present in the set of permitted values. 105 106 Objects of this type are iterable (emitting constraint values) and 107 can act as operands for some arithmetic operations e.g. addition 108 and subtraction. The latter can be used for combining multiple 109 SingleValueConstraint objects into one. 110 111 The SingleValueConstraint object can be applied to 112 any ASN.1 type. 113 114 Parameters 115 ---------- 116 *values: :class:`int` 117 Full set of values permitted by this constraint object. 118 119 Examples 120 -------- 121 .. code-block:: python 122 123 class DivisorOfSix(Integer): 124 ''' 125 ASN.1 specification: 126 127 Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6) 128 ''' 129 subtypeSpec = SingleValueConstraint(1, 2, 3, 6) 130 131 # this will succeed 132 divisor_of_six = DivisorOfSix(1) 133 134 # this will raise ValueConstraintError 135 divisor_of_six = DivisorOfSix(7) 136 """ 137 def _setValues(self, values): 138 self._values = values 139 self._set = set(values) 140 141 def _testValue(self, value, idx): 142 if value not in self._set: 143 raise error.ValueConstraintError(value) 144 145 # Constrains can be merged or reduced 146 147 def __contains__(self, item): 148 return item in self._set 149 150 def __iter__(self): 151 return iter(self._set) 152 153 def __sub__(self, constraint): 154 return self.__class__(*(self._set.difference(constraint))) 155 156 def __add__(self, constraint): 157 return self.__class__(*(self._set.union(constraint))) 158 159 def __sub__(self, constraint): 160 return self.__class__(*(self._set.difference(constraint))) 161 162 163class ContainedSubtypeConstraint(AbstractConstraint): 164 """Create a ContainedSubtypeConstraint object. 165 166 The ContainedSubtypeConstraint satisfies any value that 167 is present in the set of permitted values and also 168 satisfies included constraints. 169 170 The ContainedSubtypeConstraint object can be applied to 171 any ASN.1 type. 172 173 Parameters 174 ---------- 175 *values: 176 Full set of values and constraint objects permitted 177 by this constraint object. 178 179 Examples 180 -------- 181 .. code-block:: python 182 183 class DivisorOfEighteen(Integer): 184 ''' 185 ASN.1 specification: 186 187 Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18) 188 ''' 189 subtypeSpec = ContainedSubtypeConstraint( 190 SingleValueConstraint(1, 2, 3, 6), 9, 18 191 ) 192 193 # this will succeed 194 divisor_of_eighteen = DivisorOfEighteen(9) 195 196 # this will raise ValueConstraintError 197 divisor_of_eighteen = DivisorOfEighteen(10) 198 """ 199 def _testValue(self, value, idx): 200 for constraint in self._values: 201 if isinstance(constraint, AbstractConstraint): 202 constraint(value, idx) 203 elif value not in self._set: 204 raise error.ValueConstraintError(value) 205 206 207class ValueRangeConstraint(AbstractConstraint): 208 """Create a ValueRangeConstraint object. 209 210 The ValueRangeConstraint satisfies any value that 211 falls in the range of permitted values. 212 213 The ValueRangeConstraint object can only be applied 214 to :class:`~pyasn1.type.univ.Integer` and 215 :class:`~pyasn1.type.univ.Real` types. 216 217 Parameters 218 ---------- 219 start: :class:`int` 220 Minimum permitted value in the range (inclusive) 221 222 end: :class:`int` 223 Maximum permitted value in the range (inclusive) 224 225 Examples 226 -------- 227 .. code-block:: python 228 229 class TeenAgeYears(Integer): 230 ''' 231 ASN.1 specification: 232 233 TeenAgeYears ::= INTEGER (13 .. 19) 234 ''' 235 subtypeSpec = ValueRangeConstraint(13, 19) 236 237 # this will succeed 238 teen_year = TeenAgeYears(18) 239 240 # this will raise ValueConstraintError 241 teen_year = TeenAgeYears(20) 242 """ 243 def _testValue(self, value, idx): 244 if value < self.start or value > self.stop: 245 raise error.ValueConstraintError(value) 246 247 def _setValues(self, values): 248 if len(values) != 2: 249 raise error.PyAsn1Error( 250 '%s: bad constraint values' % (self.__class__.__name__,) 251 ) 252 self.start, self.stop = values 253 if self.start > self.stop: 254 raise error.PyAsn1Error( 255 '%s: screwed constraint values (start > stop): %s > %s' % ( 256 self.__class__.__name__, 257 self.start, self.stop 258 ) 259 ) 260 AbstractConstraint._setValues(self, values) 261 262 263class ValueSizeConstraint(ValueRangeConstraint): 264 """Create a ValueSizeConstraint object. 265 266 The ValueSizeConstraint satisfies any value for 267 as long as its size falls within the range of 268 permitted sizes. 269 270 The ValueSizeConstraint object can be applied 271 to :class:`~pyasn1.type.univ.BitString`, 272 :class:`~pyasn1.type.univ.OctetString` (including 273 all :ref:`character ASN.1 types <type.char>`), 274 :class:`~pyasn1.type.univ.SequenceOf` 275 and :class:`~pyasn1.type.univ.SetOf` types. 276 277 Parameters 278 ---------- 279 minimum: :class:`int` 280 Minimum permitted size of the value (inclusive) 281 282 maximum: :class:`int` 283 Maximum permitted size of the value (inclusive) 284 285 Examples 286 -------- 287 .. code-block:: python 288 289 class BaseballTeamRoster(SetOf): 290 ''' 291 ASN.1 specification: 292 293 BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames 294 ''' 295 componentType = PlayerNames() 296 subtypeSpec = ValueSizeConstraint(1, 25) 297 298 # this will succeed 299 team = BaseballTeamRoster() 300 team.extend(['Jan', 'Matej']) 301 encode(team) 302 303 # this will raise ValueConstraintError 304 team = BaseballTeamRoster() 305 team.extend(['Jan'] * 26) 306 encode(team) 307 308 Note 309 ---- 310 Whenever ValueSizeConstraint is applied to mutable types 311 (e.g. :class:`~pyasn1.type.univ.SequenceOf`, 312 :class:`~pyasn1.type.univ.SetOf`), constraint 313 validation only happens at the serialisation phase rather 314 than schema instantiation phase (as it is with immutable 315 types). 316 """ 317 def _testValue(self, value, idx): 318 valueSize = len(value) 319 if valueSize < self.start or valueSize > self.stop: 320 raise error.ValueConstraintError(value) 321 322 323class PermittedAlphabetConstraint(SingleValueConstraint): 324 """Create a PermittedAlphabetConstraint object. 325 326 The PermittedAlphabetConstraint satisfies any character 327 string for as long as all its characters are present in 328 the set of permitted characters. 329 330 Objects of this type are iterable (emitting constraint values) and 331 can act as operands for some arithmetic operations e.g. addition 332 and subtraction. 333 334 The PermittedAlphabetConstraint object can only be applied 335 to the :ref:`character ASN.1 types <type.char>` such as 336 :class:`~pyasn1.type.char.IA5String`. 337 338 Parameters 339 ---------- 340 *alphabet: :class:`str` 341 Full set of characters permitted by this constraint object. 342 343 Example 344 ------- 345 .. code-block:: python 346 347 class BooleanValue(IA5String): 348 ''' 349 ASN.1 specification: 350 351 BooleanValue ::= IA5String (FROM ('T' | 'F')) 352 ''' 353 subtypeSpec = PermittedAlphabetConstraint('T', 'F') 354 355 # this will succeed 356 truth = BooleanValue('T') 357 truth = BooleanValue('TF') 358 359 # this will raise ValueConstraintError 360 garbage = BooleanValue('TAF') 361 362 ASN.1 `FROM ... EXCEPT ...` clause can be modelled by combining multiple 363 PermittedAlphabetConstraint objects into one: 364 365 Example 366 ------- 367 .. code-block:: python 368 369 class Lipogramme(IA5String): 370 ''' 371 ASN.1 specification: 372 373 Lipogramme ::= 374 IA5String (FROM (ALL EXCEPT ("e"|"E"))) 375 ''' 376 subtypeSpec = ( 377 PermittedAlphabetConstraint(*string.printable) - 378 PermittedAlphabetConstraint('e', 'E') 379 ) 380 381 # this will succeed 382 lipogramme = Lipogramme('A work of fiction?') 383 384 # this will raise ValueConstraintError 385 lipogramme = Lipogramme('Eel') 386 387 Note 388 ---- 389 Although `ConstraintsExclusion` object could seemingly be used for this 390 purpose, practically, for it to work, it needs to represent its operand 391 constraints as sets and intersect one with the other. That would require 392 the insight into the constraint values (and their types) that are otherwise 393 hidden inside the constraint object. 394 395 Therefore it's more practical to model `EXCEPT` clause at 396 `PermittedAlphabetConstraint` level instead. 397 """ 398 def _setValues(self, values): 399 self._values = values 400 self._set = set(values) 401 402 def _testValue(self, value, idx): 403 if not self._set.issuperset(value): 404 raise error.ValueConstraintError(value) 405 406 407class ComponentPresentConstraint(AbstractConstraint): 408 """Create a ComponentPresentConstraint object. 409 410 The ComponentPresentConstraint is only satisfied when the value 411 is not `None`. 412 413 The ComponentPresentConstraint object is typically used with 414 `WithComponentsConstraint`. 415 416 Examples 417 -------- 418 .. code-block:: python 419 420 present = ComponentPresentConstraint() 421 422 # this will succeed 423 present('whatever') 424 425 # this will raise ValueConstraintError 426 present(None) 427 """ 428 def _setValues(self, values): 429 self._values = ('<must be present>',) 430 431 if values: 432 raise error.PyAsn1Error('No arguments expected') 433 434 def _testValue(self, value, idx): 435 if value is None: 436 raise error.ValueConstraintError( 437 'Component is not present:') 438 439 440class ComponentAbsentConstraint(AbstractConstraint): 441 """Create a ComponentAbsentConstraint object. 442 443 The ComponentAbsentConstraint is only satisfied when the value 444 is `None`. 445 446 The ComponentAbsentConstraint object is typically used with 447 `WithComponentsConstraint`. 448 449 Examples 450 -------- 451 .. code-block:: python 452 453 absent = ComponentAbsentConstraint() 454 455 # this will succeed 456 absent(None) 457 458 # this will raise ValueConstraintError 459 absent('whatever') 460 """ 461 def _setValues(self, values): 462 self._values = ('<must be absent>',) 463 464 if values: 465 raise error.PyAsn1Error('No arguments expected') 466 467 def _testValue(self, value, idx): 468 if value is not None: 469 raise error.ValueConstraintError( 470 'Component is not absent: %r' % value) 471 472 473class WithComponentsConstraint(AbstractConstraint): 474 """Create a WithComponentsConstraint object. 475 476 The `WithComponentsConstraint` satisfies any mapping object that has 477 constrained fields present or absent, what is indicated by 478 `ComponentPresentConstraint` and `ComponentAbsentConstraint` 479 objects respectively. 480 481 The `WithComponentsConstraint` object is typically applied 482 to :class:`~pyasn1.type.univ.Set` or 483 :class:`~pyasn1.type.univ.Sequence` types. 484 485 Parameters 486 ---------- 487 *fields: :class:`tuple` 488 Zero or more tuples of (`field`, `constraint`) indicating constrained 489 fields. 490 491 Notes 492 ----- 493 On top of the primary use of `WithComponentsConstraint` (ensuring presence 494 or absence of particular components of a :class:`~pyasn1.type.univ.Set` or 495 :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other 496 constraint objects or their combinations. In case of scalar fields, these 497 constraints will be verified in addition to the constraints belonging to 498 scalar components themselves. However, formally, these additional 499 constraints do not change the type of these ASN.1 objects. 500 501 Examples 502 -------- 503 504 .. code-block:: python 505 506 class Item(Sequence): # Set is similar 507 ''' 508 ASN.1 specification: 509 510 Item ::= SEQUENCE { 511 id INTEGER OPTIONAL, 512 name OCTET STRING OPTIONAL 513 } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT 514 ''' 515 componentType = NamedTypes( 516 OptionalNamedType('id', Integer()), 517 OptionalNamedType('name', OctetString()) 518 ) 519 withComponents = ConstraintsUnion( 520 WithComponentsConstraint( 521 ('id', ComponentPresentConstraint()), 522 ('name', ComponentAbsentConstraint()) 523 ), 524 WithComponentsConstraint( 525 ('id', ComponentAbsentConstraint()), 526 ('name', ComponentPresentConstraint()) 527 ) 528 ) 529 530 item = Item() 531 532 # This will succeed 533 item['id'] = 1 534 535 # This will succeed 536 item.reset() 537 item['name'] = 'John' 538 539 # This will fail (on encoding) 540 item.reset() 541 descr['id'] = 1 542 descr['name'] = 'John' 543 """ 544 def _testValue(self, value, idx): 545 for field, constraint in self._values: 546 constraint(value.get(field)) 547 548 def _setValues(self, values): 549 AbstractConstraint._setValues(self, values) 550 551 552# This is a bit kludgy, meaning two op modes within a single constraint 553class InnerTypeConstraint(AbstractConstraint): 554 """Value must satisfy the type and presence constraints""" 555 556 def _testValue(self, value, idx): 557 if self.__singleTypeConstraint: 558 self.__singleTypeConstraint(value) 559 elif self.__multipleTypeConstraint: 560 if idx not in self.__multipleTypeConstraint: 561 raise error.ValueConstraintError(value) 562 constraint, status = self.__multipleTypeConstraint[idx] 563 if status == 'ABSENT': # XXX presence is not checked! 564 raise error.ValueConstraintError(value) 565 constraint(value) 566 567 def _setValues(self, values): 568 self.__multipleTypeConstraint = {} 569 self.__singleTypeConstraint = None 570 for v in values: 571 if isinstance(v, tuple): 572 self.__multipleTypeConstraint[v[0]] = v[1], v[2] 573 else: 574 self.__singleTypeConstraint = v 575 AbstractConstraint._setValues(self, values) 576 577 578# Logic operations on constraints 579 580class ConstraintsExclusion(AbstractConstraint): 581 """Create a ConstraintsExclusion logic operator object. 582 583 The ConstraintsExclusion logic operator succeeds when the 584 value does *not* satisfy the operand constraint. 585 586 The ConstraintsExclusion object can be applied to 587 any constraint and logic operator object. 588 589 Parameters 590 ---------- 591 *constraints: 592 Constraint or logic operator objects. 593 594 Examples 595 -------- 596 .. code-block:: python 597 598 class LuckyNumber(Integer): 599 subtypeSpec = ConstraintsExclusion( 600 SingleValueConstraint(13) 601 ) 602 603 # this will succeed 604 luckyNumber = LuckyNumber(12) 605 606 # this will raise ValueConstraintError 607 luckyNumber = LuckyNumber(13) 608 609 Note 610 ---- 611 The `FROM ... EXCEPT ...` ASN.1 clause should be modeled by combining 612 constraint objects into one. See `PermittedAlphabetConstraint` for more 613 information. 614 """ 615 def _testValue(self, value, idx): 616 for constraint in self._values: 617 try: 618 constraint(value, idx) 619 620 except error.ValueConstraintError: 621 continue 622 623 raise error.ValueConstraintError(value) 624 625 def _setValues(self, values): 626 AbstractConstraint._setValues(self, values) 627 628 629class AbstractConstraintSet(AbstractConstraint): 630 631 def __getitem__(self, idx): 632 return self._values[idx] 633 634 def __iter__(self): 635 return iter(self._values) 636 637 def __add__(self, value): 638 return self.__class__(*(self._values + (value,))) 639 640 def __radd__(self, value): 641 return self.__class__(*((value,) + self._values)) 642 643 def __len__(self): 644 return len(self._values) 645 646 # Constraints inclusion in sets 647 648 def _setValues(self, values): 649 self._values = values 650 for constraint in values: 651 if constraint: 652 self._valueMap.add(constraint) 653 self._valueMap.update(constraint.getValueMap()) 654 655 656class ConstraintsIntersection(AbstractConstraintSet): 657 """Create a ConstraintsIntersection logic operator object. 658 659 The ConstraintsIntersection logic operator only succeeds 660 if *all* its operands succeed. 661 662 The ConstraintsIntersection object can be applied to 663 any constraint and logic operator objects. 664 665 The ConstraintsIntersection object duck-types the immutable 666 container object like Python :py:class:`tuple`. 667 668 Parameters 669 ---------- 670 *constraints: 671 Constraint or logic operator objects. 672 673 Examples 674 -------- 675 .. code-block:: python 676 677 class CapitalAndSmall(IA5String): 678 ''' 679 ASN.1 specification: 680 681 CapitalAndSmall ::= 682 IA5String (FROM ("A".."Z"|"a".."z")) 683 ''' 684 subtypeSpec = ConstraintsIntersection( 685 PermittedAlphabetConstraint('A', 'Z'), 686 PermittedAlphabetConstraint('a', 'z') 687 ) 688 689 # this will succeed 690 capital_and_small = CapitalAndSmall('Hello') 691 692 # this will raise ValueConstraintError 693 capital_and_small = CapitalAndSmall('hello') 694 """ 695 def _testValue(self, value, idx): 696 for constraint in self._values: 697 constraint(value, idx) 698 699 700class ConstraintsUnion(AbstractConstraintSet): 701 """Create a ConstraintsUnion logic operator object. 702 703 The ConstraintsUnion logic operator succeeds if 704 *at least* a single operand succeeds. 705 706 The ConstraintsUnion object can be applied to 707 any constraint and logic operator objects. 708 709 The ConstraintsUnion object duck-types the immutable 710 container object like Python :py:class:`tuple`. 711 712 Parameters 713 ---------- 714 *constraints: 715 Constraint or logic operator objects. 716 717 Examples 718 -------- 719 .. code-block:: python 720 721 class CapitalOrSmall(IA5String): 722 ''' 723 ASN.1 specification: 724 725 CapitalOrSmall ::= 726 IA5String (FROM ("A".."Z") | FROM ("a".."z")) 727 ''' 728 subtypeSpec = ConstraintsUnion( 729 PermittedAlphabetConstraint('A', 'Z'), 730 PermittedAlphabetConstraint('a', 'z') 731 ) 732 733 # this will succeed 734 capital_or_small = CapitalAndSmall('Hello') 735 736 # this will raise ValueConstraintError 737 capital_or_small = CapitalOrSmall('hello!') 738 """ 739 def _testValue(self, value, idx): 740 for constraint in self._values: 741 try: 742 constraint(value, idx) 743 except error.ValueConstraintError: 744 pass 745 else: 746 return 747 748 raise error.ValueConstraintError( 749 'all of %s failed for "%s"' % (self._values, value) 750 ) 751 752# TODO: 753# refactor InnerTypeConstraint 754# add tests for type check 755# implement other constraint types 756# make constraint validation easy to skip 757