201 lines
7.1 KiB
Python
201 lines
7.1 KiB
Python
#
|
|
# ASN.1 subtype constraints classes.
|
|
#
|
|
# Constraints are relatively rare, but every ASN1 object
|
|
# is doing checks all the time for whether they have any
|
|
# constraints and whether they are applicable to the object.
|
|
#
|
|
# What we're going to do is define objects/functions that
|
|
# can be called unconditionally if they are present, and that
|
|
# are simply not present if there are no constraints.
|
|
#
|
|
# Original concept and code by Mike C. Fletcher.
|
|
#
|
|
import sys
|
|
from pyasn1.type import error
|
|
|
|
class AbstractConstraint:
|
|
"""Abstract base-class for constraint objects
|
|
|
|
Constraints should be stored in a simple sequence in the
|
|
namespace of their client Asn1Item sub-classes.
|
|
"""
|
|
def __init__(self, *values):
|
|
self._valueMap = {}
|
|
self._setValues(values)
|
|
self.__hashedValues = None
|
|
def __call__(self, value, idx=None):
|
|
try:
|
|
self._testValue(value, idx)
|
|
except error.ValueConstraintError:
|
|
raise error.ValueConstraintError(
|
|
'%s failed at: \"%s\"' % (self, sys.exc_info()[1])
|
|
)
|
|
def __repr__(self):
|
|
return '%s(%s)' % (
|
|
self.__class__.__name__,
|
|
', '.join([repr(x) for x in self._values])
|
|
)
|
|
def __eq__(self, other):
|
|
return self is other and True or self._values == other
|
|
def __ne__(self, other): return self._values != other
|
|
def __lt__(self, other): return self._values < other
|
|
def __le__(self, other): return self._values <= other
|
|
def __gt__(self, other): return self._values > other
|
|
def __ge__(self, other): return self._values >= other
|
|
if sys.version_info[0] <= 2:
|
|
def __nonzero__(self): return bool(self._values)
|
|
else:
|
|
def __bool__(self): return bool(self._values)
|
|
|
|
def __hash__(self):
|
|
if self.__hashedValues is None:
|
|
self.__hashedValues = hash((self.__class__.__name__, self._values))
|
|
return self.__hashedValues
|
|
|
|
def _setValues(self, values): self._values = values
|
|
def _testValue(self, value, idx):
|
|
raise error.ValueConstraintError(value)
|
|
|
|
# Constraints derivation logic
|
|
def getValueMap(self): return self._valueMap
|
|
def isSuperTypeOf(self, otherConstraint):
|
|
return self in otherConstraint.getValueMap() or \
|
|
otherConstraint is self or otherConstraint == self
|
|
def isSubTypeOf(self, otherConstraint):
|
|
return otherConstraint in self._valueMap or \
|
|
otherConstraint is self or otherConstraint == self
|
|
|
|
class SingleValueConstraint(AbstractConstraint):
|
|
"""Value must be part of defined values constraint"""
|
|
def _testValue(self, value, idx):
|
|
# XXX index vals for performance?
|
|
if value not in self._values:
|
|
raise error.ValueConstraintError(value)
|
|
|
|
class ContainedSubtypeConstraint(AbstractConstraint):
|
|
"""Value must satisfy all of defined set of constraints"""
|
|
def _testValue(self, value, idx):
|
|
for c in self._values:
|
|
c(value, idx)
|
|
|
|
class ValueRangeConstraint(AbstractConstraint):
|
|
"""Value must be within start and stop values (inclusive)"""
|
|
def _testValue(self, value, idx):
|
|
if value < self.start or value > self.stop:
|
|
raise error.ValueConstraintError(value)
|
|
|
|
def _setValues(self, values):
|
|
if len(values) != 2:
|
|
raise error.PyAsn1Error(
|
|
'%s: bad constraint values' % (self.__class__.__name__,)
|
|
)
|
|
self.start, self.stop = values
|
|
if self.start > self.stop:
|
|
raise error.PyAsn1Error(
|
|
'%s: screwed constraint values (start > stop): %s > %s' % (
|
|
self.__class__.__name__,
|
|
self.start, self.stop
|
|
)
|
|
)
|
|
AbstractConstraint._setValues(self, values)
|
|
|
|
class ValueSizeConstraint(ValueRangeConstraint):
|
|
"""len(value) must be within start and stop values (inclusive)"""
|
|
def _testValue(self, value, idx):
|
|
l = len(value)
|
|
if l < self.start or l > self.stop:
|
|
raise error.ValueConstraintError(value)
|
|
|
|
class PermittedAlphabetConstraint(SingleValueConstraint):
|
|
def _setValues(self, values):
|
|
self._values = ()
|
|
for v in values:
|
|
self._values = self._values + tuple(v)
|
|
|
|
def _testValue(self, value, idx):
|
|
for v in value:
|
|
if v not in self._values:
|
|
raise error.ValueConstraintError(value)
|
|
|
|
# This is a bit kludgy, meaning two op modes within a single constraing
|
|
class InnerTypeConstraint(AbstractConstraint):
|
|
"""Value must satisfy type and presense constraints"""
|
|
def _testValue(self, value, idx):
|
|
if self.__singleTypeConstraint:
|
|
self.__singleTypeConstraint(value)
|
|
elif self.__multipleTypeConstraint:
|
|
if idx not in self.__multipleTypeConstraint:
|
|
raise error.ValueConstraintError(value)
|
|
constraint, status = self.__multipleTypeConstraint[idx]
|
|
if status == 'ABSENT': # XXX presense is not checked!
|
|
raise error.ValueConstraintError(value)
|
|
constraint(value)
|
|
|
|
def _setValues(self, values):
|
|
self.__multipleTypeConstraint = {}
|
|
self.__singleTypeConstraint = None
|
|
for v in values:
|
|
if isinstance(v, tuple):
|
|
self.__multipleTypeConstraint[v[0]] = v[1], v[2]
|
|
else:
|
|
self.__singleTypeConstraint = v
|
|
AbstractConstraint._setValues(self, values)
|
|
|
|
# Boolean ops on constraints
|
|
|
|
class ConstraintsExclusion(AbstractConstraint):
|
|
"""Value must not fit the single constraint"""
|
|
def _testValue(self, value, idx):
|
|
try:
|
|
self._values[0](value, idx)
|
|
except error.ValueConstraintError:
|
|
return
|
|
else:
|
|
raise error.ValueConstraintError(value)
|
|
|
|
def _setValues(self, values):
|
|
if len(values) != 1:
|
|
raise error.PyAsn1Error('Single constraint expected')
|
|
AbstractConstraint._setValues(self, values)
|
|
|
|
class AbstractConstraintSet(AbstractConstraint):
|
|
"""Value must not satisfy the single constraint"""
|
|
def __getitem__(self, idx): return self._values[idx]
|
|
|
|
def __add__(self, value): return self.__class__(self, value)
|
|
def __radd__(self, value): return self.__class__(self, value)
|
|
|
|
def __len__(self): return len(self._values)
|
|
|
|
# Constraints inclusion in sets
|
|
|
|
def _setValues(self, values):
|
|
self._values = values
|
|
for v in values:
|
|
self._valueMap[v] = 1
|
|
self._valueMap.update(v.getValueMap())
|
|
|
|
class ConstraintsIntersection(AbstractConstraintSet):
|
|
"""Value must satisfy all constraints"""
|
|
def _testValue(self, value, idx):
|
|
for v in self._values:
|
|
v(value, idx)
|
|
|
|
class ConstraintsUnion(AbstractConstraintSet):
|
|
"""Value must satisfy at least one constraint"""
|
|
def _testValue(self, value, idx):
|
|
for v in self._values:
|
|
try:
|
|
v(value, idx)
|
|
except error.ValueConstraintError:
|
|
pass
|
|
else:
|
|
return
|
|
raise error.ValueConstraintError(
|
|
'all of %s failed for \"%s\"' % (self._values, value)
|
|
)
|
|
|
|
# XXX
|
|
# add tests for type check
|