1# 2# ASN.1 subtype constraints classes. 3# 4# Constraints are relatively rare, but every ASN1 object 5# is doing checks all the time for whether they have any 6# constraints and whether they are applicable to the object. 7# 8# What we're going to do is define objects/functions that 9# can be called unconditionally if they are present, and that 10# are simply not present if there are no constraints. 11# 12# Original concept and code by Mike C. Fletcher. 13# 14import sys 15from pyasn1.type import error 16 17class AbstractConstraint: 18 """Abstract base-class for constraint objects 19 20 Constraints should be stored in a simple sequence in the 21 namespace of their client Asn1Item sub-classes. 22 """ 23 def __init__(self, *values): 24 self._valueMap = {} 25 self._setValues(values) 26 self.__hashedValues = None 27 def __call__(self, value, idx=None): 28 try: 29 self._testValue(value, idx) 30 except error.ValueConstraintError: 31 raise error.ValueConstraintError( 32 '%s failed at: \"%s\"' % (self, sys.exc_info()[1]) 33 ) 34 def __repr__(self): 35 return '%s(%s)' % ( 36 self.__class__.__name__, 37 ', '.join([repr(x) for x in self._values]) 38 ) 39 def __eq__(self, other): 40 return self is other and True or self._values == other 41 def __ne__(self, other): return self._values != other 42 def __lt__(self, other): return self._values < other 43 def __le__(self, other): return self._values <= other 44 def __gt__(self, other): return self._values > other 45 def __ge__(self, other): return self._values >= other 46 if sys.version_info[0] <= 2: 47 def __nonzero__(self): return bool(self._values) 48 else: 49 def __bool__(self): return bool(self._values) 50 51 def __hash__(self): 52 if self.__hashedValues is None: 53 self.__hashedValues = hash((self.__class__.__name__, self._values)) 54 return self.__hashedValues 55 56 def _setValues(self, values): self._values = values 57 def _testValue(self, value, idx): 58 raise error.ValueConstraintError(value) 59 60 # Constraints derivation logic 61 def getValueMap(self): return self._valueMap 62 def isSuperTypeOf(self, otherConstraint): 63 return self in otherConstraint.getValueMap() or \ 64 otherConstraint is self or otherConstraint == self 65 def isSubTypeOf(self, otherConstraint): 66 return otherConstraint in self._valueMap or \ 67 otherConstraint is self or otherConstraint == self 68 69class SingleValueConstraint(AbstractConstraint): 70 """Value must be part of defined values constraint""" 71 def _testValue(self, value, idx): 72 # XXX index vals for performance? 73 if value not in self._values: 74 raise error.ValueConstraintError(value) 75 76class ContainedSubtypeConstraint(AbstractConstraint): 77 """Value must satisfy all of defined set of constraints""" 78 def _testValue(self, value, idx): 79 for c in self._values: 80 c(value, idx) 81 82class ValueRangeConstraint(AbstractConstraint): 83 """Value must be within start and stop values (inclusive)""" 84 def _testValue(self, value, idx): 85 if value < self.start or value > self.stop: 86 raise error.ValueConstraintError(value) 87 88 def _setValues(self, values): 89 if len(values) != 2: 90 raise error.PyAsn1Error( 91 '%s: bad constraint values' % (self.__class__.__name__,) 92 ) 93 self.start, self.stop = values 94 if self.start > self.stop: 95 raise error.PyAsn1Error( 96 '%s: screwed constraint values (start > stop): %s > %s' % ( 97 self.__class__.__name__, 98 self.start, self.stop 99 ) 100 ) 101 AbstractConstraint._setValues(self, values) 102 103class ValueSizeConstraint(ValueRangeConstraint): 104 """len(value) must be within start and stop values (inclusive)""" 105 def _testValue(self, value, idx): 106 l = len(value) 107 if l < self.start or l > self.stop: 108 raise error.ValueConstraintError(value) 109 110class PermittedAlphabetConstraint(SingleValueConstraint): 111 def _setValues(self, values): 112 self._values = () 113 for v in values: 114 self._values = self._values + tuple(v) 115 116 def _testValue(self, value, idx): 117 for v in value: 118 if v not in self._values: 119 raise error.ValueConstraintError(value) 120 121# This is a bit kludgy, meaning two op modes within a single constraing 122class InnerTypeConstraint(AbstractConstraint): 123 """Value must satisfy type and presense constraints""" 124 def _testValue(self, value, idx): 125 if self.__singleTypeConstraint: 126 self.__singleTypeConstraint(value) 127 elif self.__multipleTypeConstraint: 128 if idx not in self.__multipleTypeConstraint: 129 raise error.ValueConstraintError(value) 130 constraint, status = self.__multipleTypeConstraint[idx] 131 if status == 'ABSENT': # XXX presense is not checked! 132 raise error.ValueConstraintError(value) 133 constraint(value) 134 135 def _setValues(self, values): 136 self.__multipleTypeConstraint = {} 137 self.__singleTypeConstraint = None 138 for v in values: 139 if isinstance(v, tuple): 140 self.__multipleTypeConstraint[v[0]] = v[1], v[2] 141 else: 142 self.__singleTypeConstraint = v 143 AbstractConstraint._setValues(self, values) 144 145# Boolean ops on constraints 146 147class ConstraintsExclusion(AbstractConstraint): 148 """Value must not fit the single constraint""" 149 def _testValue(self, value, idx): 150 try: 151 self._values[0](value, idx) 152 except error.ValueConstraintError: 153 return 154 else: 155 raise error.ValueConstraintError(value) 156 157 def _setValues(self, values): 158 if len(values) != 1: 159 raise error.PyAsn1Error('Single constraint expected') 160 AbstractConstraint._setValues(self, values) 161 162class AbstractConstraintSet(AbstractConstraint): 163 """Value must not satisfy the single constraint""" 164 def __getitem__(self, idx): return self._values[idx] 165 166 def __add__(self, value): return self.__class__(self, value) 167 def __radd__(self, value): return self.__class__(self, value) 168 169 def __len__(self): return len(self._values) 170 171 # Constraints inclusion in sets 172 173 def _setValues(self, values): 174 self._values = values 175 for v in values: 176 self._valueMap[v] = 1 177 self._valueMap.update(v.getValueMap()) 178 179class ConstraintsIntersection(AbstractConstraintSet): 180 """Value must satisfy all constraints""" 181 def _testValue(self, value, idx): 182 for v in self._values: 183 v(value, idx) 184 185class ConstraintsUnion(AbstractConstraintSet): 186 """Value must satisfy at least one constraint""" 187 def _testValue(self, value, idx): 188 for v in self._values: 189 try: 190 v(value, idx) 191 except error.ValueConstraintError: 192 pass 193 else: 194 return 195 raise error.ValueConstraintError( 196 'all of %s failed for \"%s\"' % (self._values, value) 197 ) 198 199# XXX 200# add tests for type check 201