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 The SingleValueConstraint object can be applied to 107 any ASN.1 type. 108 109 Parameters 110 ---------- 111 *values: :class:`int` 112 Full set of values permitted by this constraint object. 113 114 Examples 115 -------- 116 .. code-block:: python 117 118 class DivisorOfSix(Integer): 119 ''' 120 ASN.1 specification: 121 122 Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6) 123 ''' 124 subtypeSpec = SingleValueConstraint(1, 2, 3, 6) 125 126 # this will succeed 127 divisor_of_six = DivisorOfSix(1) 128 129 # this will raise ValueConstraintError 130 divisor_of_six = DivisorOfSix(7) 131 """ 132 def _setValues(self, values): 133 self._values = values 134 self._set = set(values) 135 136 def _testValue(self, value, idx): 137 if value not in self._set: 138 raise error.ValueConstraintError(value) 139 140 141class ContainedSubtypeConstraint(AbstractConstraint): 142 """Create a ContainedSubtypeConstraint object. 143 144 The ContainedSubtypeConstraint satisfies any value that 145 is present in the set of permitted values and also 146 satisfies included constraints. 147 148 The ContainedSubtypeConstraint object can be applied to 149 any ASN.1 type. 150 151 Parameters 152 ---------- 153 *values: 154 Full set of values and constraint objects permitted 155 by this constraint object. 156 157 Examples 158 -------- 159 .. code-block:: python 160 161 class DivisorOfEighteen(Integer): 162 ''' 163 ASN.1 specification: 164 165 Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18) 166 ''' 167 subtypeSpec = ContainedSubtypeConstraint( 168 SingleValueConstraint(1, 2, 3, 6), 9, 18 169 ) 170 171 # this will succeed 172 divisor_of_eighteen = DivisorOfEighteen(9) 173 174 # this will raise ValueConstraintError 175 divisor_of_eighteen = DivisorOfEighteen(10) 176 """ 177 def _testValue(self, value, idx): 178 for constraint in self._values: 179 if isinstance(constraint, AbstractConstraint): 180 constraint(value, idx) 181 elif value not in self._set: 182 raise error.ValueConstraintError(value) 183 184 185class ValueRangeConstraint(AbstractConstraint): 186 """Create a ValueRangeConstraint object. 187 188 The ValueRangeConstraint satisfies any value that 189 falls in the range of permitted values. 190 191 The ValueRangeConstraint object can only be applied 192 to :class:`~pyasn1.type.univ.Integer` and 193 :class:`~pyasn1.type.univ.Real` types. 194 195 Parameters 196 ---------- 197 start: :class:`int` 198 Minimum permitted value in the range (inclusive) 199 200 end: :class:`int` 201 Maximum permitted value in the range (inclusive) 202 203 Examples 204 -------- 205 .. code-block:: python 206 207 class TeenAgeYears(Integer): 208 ''' 209 ASN.1 specification: 210 211 TeenAgeYears ::= INTEGER (13 .. 19) 212 ''' 213 subtypeSpec = ValueRangeConstraint(13, 19) 214 215 # this will succeed 216 teen_year = TeenAgeYears(18) 217 218 # this will raise ValueConstraintError 219 teen_year = TeenAgeYears(20) 220 """ 221 def _testValue(self, value, idx): 222 if value < self.start or value > self.stop: 223 raise error.ValueConstraintError(value) 224 225 def _setValues(self, values): 226 if len(values) != 2: 227 raise error.PyAsn1Error( 228 '%s: bad constraint values' % (self.__class__.__name__,) 229 ) 230 self.start, self.stop = values 231 if self.start > self.stop: 232 raise error.PyAsn1Error( 233 '%s: screwed constraint values (start > stop): %s > %s' % ( 234 self.__class__.__name__, 235 self.start, self.stop 236 ) 237 ) 238 AbstractConstraint._setValues(self, values) 239 240 241class ValueSizeConstraint(ValueRangeConstraint): 242 """Create a ValueSizeConstraint object. 243 244 The ValueSizeConstraint satisfies any value for 245 as long as its size falls within the range of 246 permitted sizes. 247 248 The ValueSizeConstraint object can be applied 249 to :class:`~pyasn1.type.univ.BitString`, 250 :class:`~pyasn1.type.univ.OctetString` (including 251 all :ref:`character ASN.1 types <type.char>`), 252 :class:`~pyasn1.type.univ.SequenceOf` 253 and :class:`~pyasn1.type.univ.SetOf` types. 254 255 Parameters 256 ---------- 257 minimum: :class:`int` 258 Minimum permitted size of the value (inclusive) 259 260 maximum: :class:`int` 261 Maximum permitted size of the value (inclusive) 262 263 Examples 264 -------- 265 .. code-block:: python 266 267 class BaseballTeamRoster(SetOf): 268 ''' 269 ASN.1 specification: 270 271 BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames 272 ''' 273 componentType = PlayerNames() 274 subtypeSpec = ValueSizeConstraint(1, 25) 275 276 # this will succeed 277 team = BaseballTeamRoster() 278 team.extend(['Jan', 'Matej']) 279 encode(team) 280 281 # this will raise ValueConstraintError 282 team = BaseballTeamRoster() 283 team.extend(['Jan'] * 26) 284 encode(team) 285 286 Note 287 ---- 288 Whenever ValueSizeConstraint is applied to mutable types 289 (e.g. :class:`~pyasn1.type.univ.SequenceOf`, 290 :class:`~pyasn1.type.univ.SetOf`), constraint 291 validation only happens at the serialisation phase rather 292 than schema instantiation phase (as it is with immutable 293 types). 294 """ 295 def _testValue(self, value, idx): 296 valueSize = len(value) 297 if valueSize < self.start or valueSize > self.stop: 298 raise error.ValueConstraintError(value) 299 300 301class PermittedAlphabetConstraint(SingleValueConstraint): 302 """Create a PermittedAlphabetConstraint object. 303 304 The PermittedAlphabetConstraint satisfies any character 305 string for as long as all its characters are present in 306 the set of permitted characters. 307 308 The PermittedAlphabetConstraint object can only be applied 309 to the :ref:`character ASN.1 types <type.char>` such as 310 :class:`~pyasn1.type.char.IA5String`. 311 312 Parameters 313 ---------- 314 *alphabet: :class:`str` 315 Full set of characters permitted by this constraint object. 316 317 Examples 318 -------- 319 .. code-block:: python 320 321 class BooleanValue(IA5String): 322 ''' 323 ASN.1 specification: 324 325 BooleanValue ::= IA5String (FROM ('T' | 'F')) 326 ''' 327 subtypeSpec = PermittedAlphabetConstraint('T', 'F') 328 329 # this will succeed 330 truth = BooleanValue('T') 331 truth = BooleanValue('TF') 332 333 # this will raise ValueConstraintError 334 garbage = BooleanValue('TAF') 335 """ 336 def _setValues(self, values): 337 self._values = values 338 self._set = set(values) 339 340 def _testValue(self, value, idx): 341 if not self._set.issuperset(value): 342 raise error.ValueConstraintError(value) 343 344 345class ComponentPresentConstraint(AbstractConstraint): 346 """Create a ComponentPresentConstraint object. 347 348 The ComponentPresentConstraint is only satisfied when the value 349 is not `None`. 350 351 The ComponentPresentConstraint object is typically used with 352 `WithComponentsConstraint`. 353 354 Examples 355 -------- 356 .. code-block:: python 357 358 present = ComponentPresentConstraint() 359 360 # this will succeed 361 present('whatever') 362 363 # this will raise ValueConstraintError 364 present(None) 365 """ 366 def _setValues(self, values): 367 self._values = ('<must be present>',) 368 369 if values: 370 raise error.PyAsn1Error('No arguments expected') 371 372 def _testValue(self, value, idx): 373 if value is None: 374 raise error.ValueConstraintError( 375 'Component is not present:') 376 377 378class ComponentAbsentConstraint(AbstractConstraint): 379 """Create a ComponentAbsentConstraint object. 380 381 The ComponentAbsentConstraint is only satisfied when the value 382 is `None`. 383 384 The ComponentAbsentConstraint object is typically used with 385 `WithComponentsConstraint`. 386 387 Examples 388 -------- 389 .. code-block:: python 390 391 absent = ComponentAbsentConstraint() 392 393 # this will succeed 394 absent(None) 395 396 # this will raise ValueConstraintError 397 absent('whatever') 398 """ 399 def _setValues(self, values): 400 self._values = ('<must be absent>',) 401 402 if values: 403 raise error.PyAsn1Error('No arguments expected') 404 405 def _testValue(self, value, idx): 406 if value is not None: 407 raise error.ValueConstraintError( 408 'Component is not absent: %r' % value) 409 410 411class WithComponentsConstraint(AbstractConstraint): 412 """Create a WithComponentsConstraint object. 413 414 The `WithComponentsConstraint` satisfies any mapping object that has 415 constrained fields present or absent, what is indicated by 416 `ComponentPresentConstraint` and `ComponentAbsentConstraint` 417 objects respectively. 418 419 The `WithComponentsConstraint` object is typically applied 420 to :class:`~pyasn1.type.univ.Set` or 421 :class:`~pyasn1.type.univ.Sequence` types. 422 423 Parameters 424 ---------- 425 *fields: :class:`tuple` 426 Zero or more tuples of (`field`, `constraint`) indicating constrained 427 fields. 428 429 Notes 430 ----- 431 On top of the primary use of `WithComponentsConstraint` (ensuring presence 432 or absence of particular components of a :class:`~pyasn1.type.univ.Set` or 433 :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other 434 constraint objects or their combinations. In case of scalar fields, these 435 constraints will be verified in addition to the constraints belonging to 436 scalar components themselves. However, formally, these additional 437 constraints do not change the type of these ASN.1 objects. 438 439 Examples 440 -------- 441 442 .. code-block:: python 443 444 class Item(Sequence): # Set is similar 445 ''' 446 ASN.1 specification: 447 448 Item ::= SEQUENCE { 449 id INTEGER OPTIONAL, 450 name OCTET STRING OPTIONAL 451 } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT 452 ''' 453 componentType = NamedTypes( 454 OptionalNamedType('id', Integer()), 455 OptionalNamedType('name', OctetString()) 456 ) 457 withComponents = ConstraintsUnion( 458 WithComponentsConstraint( 459 ('id', ComponentPresentConstraint()), 460 ('name', ComponentAbsentConstraint()) 461 ), 462 WithComponentsConstraint( 463 ('id', ComponentAbsentConstraint()), 464 ('name', ComponentPresentConstraint()) 465 ) 466 ) 467 468 item = Item() 469 470 # This will succeed 471 item['id'] = 1 472 473 # This will succeed 474 item.reset() 475 item['name'] = 'John' 476 477 # This will fail (on encoding) 478 item.reset() 479 descr['id'] = 1 480 descr['name'] = 'John' 481 """ 482 def _testValue(self, value, idx): 483 for field, constraint in self._values: 484 constraint(value.get(field)) 485 486 def _setValues(self, values): 487 AbstractConstraint._setValues(self, values) 488 489 490# This is a bit kludgy, meaning two op modes within a single constraint 491class InnerTypeConstraint(AbstractConstraint): 492 """Value must satisfy the type and presence constraints""" 493 494 def _testValue(self, value, idx): 495 if self.__singleTypeConstraint: 496 self.__singleTypeConstraint(value) 497 elif self.__multipleTypeConstraint: 498 if idx not in self.__multipleTypeConstraint: 499 raise error.ValueConstraintError(value) 500 constraint, status = self.__multipleTypeConstraint[idx] 501 if status == 'ABSENT': # XXX presence is not checked! 502 raise error.ValueConstraintError(value) 503 constraint(value) 504 505 def _setValues(self, values): 506 self.__multipleTypeConstraint = {} 507 self.__singleTypeConstraint = None 508 for v in values: 509 if isinstance(v, tuple): 510 self.__multipleTypeConstraint[v[0]] = v[1], v[2] 511 else: 512 self.__singleTypeConstraint = v 513 AbstractConstraint._setValues(self, values) 514 515 516# Logic operations on constraints 517 518class ConstraintsExclusion(AbstractConstraint): 519 """Create a ConstraintsExclusion logic operator object. 520 521 The ConstraintsExclusion logic operator succeeds when the 522 value does *not* satisfy the operand constraint. 523 524 The ConstraintsExclusion object can be applied to 525 any constraint and logic operator object. 526 527 Parameters 528 ---------- 529 constraint: 530 Constraint or logic operator object. 531 532 Examples 533 -------- 534 .. code-block:: python 535 536 class Lipogramme(IA5STRING): 537 ''' 538 ASN.1 specification: 539 540 Lipogramme ::= 541 IA5String (FROM (ALL EXCEPT ("e"|"E"))) 542 ''' 543 subtypeSpec = ConstraintsExclusion( 544 PermittedAlphabetConstraint('e', 'E') 545 ) 546 547 # this will succeed 548 lipogramme = Lipogramme('A work of fiction?') 549 550 # this will raise ValueConstraintError 551 lipogramme = Lipogramme('Eel') 552 553 Warning 554 ------- 555 The above example involving PermittedAlphabetConstraint might 556 not work due to the way how PermittedAlphabetConstraint works. 557 The other constraints might work with ConstraintsExclusion 558 though. 559 """ 560 def _testValue(self, value, idx): 561 try: 562 self._values[0](value, idx) 563 except error.ValueConstraintError: 564 return 565 else: 566 raise error.ValueConstraintError(value) 567 568 def _setValues(self, values): 569 if len(values) != 1: 570 raise error.PyAsn1Error('Single constraint expected') 571 572 AbstractConstraint._setValues(self, values) 573 574 575class AbstractConstraintSet(AbstractConstraint): 576 577 def __getitem__(self, idx): 578 return self._values[idx] 579 580 def __iter__(self): 581 return iter(self._values) 582 583 def __add__(self, value): 584 return self.__class__(*(self._values + (value,))) 585 586 def __radd__(self, value): 587 return self.__class__(*((value,) + self._values)) 588 589 def __len__(self): 590 return len(self._values) 591 592 # Constraints inclusion in sets 593 594 def _setValues(self, values): 595 self._values = values 596 for constraint in values: 597 if constraint: 598 self._valueMap.add(constraint) 599 self._valueMap.update(constraint.getValueMap()) 600 601 602class ConstraintsIntersection(AbstractConstraintSet): 603 """Create a ConstraintsIntersection logic operator object. 604 605 The ConstraintsIntersection logic operator only succeeds 606 if *all* its operands succeed. 607 608 The ConstraintsIntersection object can be applied to 609 any constraint and logic operator objects. 610 611 The ConstraintsIntersection object duck-types the immutable 612 container object like Python :py:class:`tuple`. 613 614 Parameters 615 ---------- 616 *constraints: 617 Constraint or logic operator objects. 618 619 Examples 620 -------- 621 .. code-block:: python 622 623 class CapitalAndSmall(IA5String): 624 ''' 625 ASN.1 specification: 626 627 CapitalAndSmall ::= 628 IA5String (FROM ("A".."Z"|"a".."z")) 629 ''' 630 subtypeSpec = ConstraintsIntersection( 631 PermittedAlphabetConstraint('A', 'Z'), 632 PermittedAlphabetConstraint('a', 'z') 633 ) 634 635 # this will succeed 636 capital_and_small = CapitalAndSmall('Hello') 637 638 # this will raise ValueConstraintError 639 capital_and_small = CapitalAndSmall('hello') 640 """ 641 def _testValue(self, value, idx): 642 for constraint in self._values: 643 constraint(value, idx) 644 645 646class ConstraintsUnion(AbstractConstraintSet): 647 """Create a ConstraintsUnion logic operator object. 648 649 The ConstraintsUnion logic operator succeeds if 650 *at least* a single operand succeeds. 651 652 The ConstraintsUnion object can be applied to 653 any constraint and logic operator objects. 654 655 The ConstraintsUnion object duck-types the immutable 656 container object like Python :py:class:`tuple`. 657 658 Parameters 659 ---------- 660 *constraints: 661 Constraint or logic operator objects. 662 663 Examples 664 -------- 665 .. code-block:: python 666 667 class CapitalOrSmall(IA5String): 668 ''' 669 ASN.1 specification: 670 671 CapitalOrSmall ::= 672 IA5String (FROM ("A".."Z") | FROM ("a".."z")) 673 ''' 674 subtypeSpec = ConstraintsUnion( 675 PermittedAlphabetConstraint('A', 'Z'), 676 PermittedAlphabetConstraint('a', 'z') 677 ) 678 679 # this will succeed 680 capital_or_small = CapitalAndSmall('Hello') 681 682 # this will raise ValueConstraintError 683 capital_or_small = CapitalOrSmall('hello!') 684 """ 685 def _testValue(self, value, idx): 686 for constraint in self._values: 687 try: 688 constraint(value, idx) 689 except error.ValueConstraintError: 690 pass 691 else: 692 return 693 694 raise error.ValueConstraintError( 695 'all of %s failed for "%s"' % (self._values, value) 696 ) 697 698# TODO: 699# refactor InnerTypeConstraint 700# add tests for type check 701# implement other constraint types 702# make constraint validation easy to skip 703