Changeset 5162
 Timestamp:
 02/04/09 10:38:41 (11 years ago)
 Location:
 TI12security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml
 Files:

 1 deleted
 2 edited
Legend:
 Unmodified
 Added
 Removed

TI12security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/__init__.py
r5161 r5162 1 """XACML P olicy Decision Point module1 """XACML Package 2 2 3 3 NERC DataGrid Project … … 354 354 raise NotImplementedError() 355 355 356 357 class FunctionBase(XacmlBase):358 FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:"359 360 def __init__(self,361 functionName,362 functionId=None,363 paramType=None,364 paramIsBag=False,365 numParams=0,366 minParams=0,367 returnType='',368 returnsBag=False):369 370 self.functionName = functionName371 self.functionId = functionId372 self.returnType = None373 self.returnsBag = False374 375 self.singleType = True;376 377 self.paramType = paramType378 self.paramIsBag = paramIsBag379 self.numParams = numParams380 self.minParams = minParams381 382 383 def _setFunctionName(self, functionName):384 if functionName not in self.__class__.supportedIdentifiers:385 functionList = ', '.join(self.__class__.supportedIdentifiers)386 raise TypeError("Function name [%s] is not on of the recognised "387 "types: %s" % (functionName, functionList))388 self._functionName = functionName389 390 def _getFunctionName(self):391 return getattr(self, '_functionName', None)392 393 functionName = property(fset=_setFunctionName,394 fget=_getFunctionName)395 396 def checkInputs(self, inputs):397 '''Checks that the given inputs are of the right types, in the right398 order, and are the right number for this function to evaluate.'''399 raise NotImplementedError()400 401 def checkInputsNoBag(self, inputs):402 '''Checks that the given inputs are of the right types, in the right403 order, and are the right number for this function to evaluate.'''404 raise NotImplementedError()405 406 def evaluate(self, inputs, context):407 '''Evaluates the Function using the given inputs.'''408 raise NotImplementedError()409 410 def evalArgs(self, params, context, args):411 '''Evaluates each of the parameters, in order, filling in the argument412 array with the resulting values. If any error occurs, this method413 returns the error, otherwise null is returned, signalling that414 evaluation was successful for all inputs, and the resulting argument415 list can be used.416 417 @param params a list of Evaluatable objects representing the parameters418 to evaluate419 @param context the representation of the request420 @param args an array as long as the params list that will, on return,421 contain the AttributeValues generated from evaluating all parameters422 423 @return None if no errors were encountered, otherwise424 an EvaluationResult representing the error425 '''426 index = 0427 428 for eval in params:429 # get and evaluate the next parameter430 result = eval.evaluate(context)431 432 # If there was an error, pass it back...433 if result.indeterminate():434 return result435 436 # ...otherwise save it and keep going437 args[index] = result.getAttributeValue()438 index += 1439 440 return None441 442 # TODO: Condition classes  minimal implementation until opportunity to fully443 # implement444 class Function(XacmlBase):445 def __init__(self, *arg, **kw):446 raise NotImplementedError()447 448 @classmethod449 def getInstance(cls, root):450 raise NotImplementedError()451 452 class BagFunction(FunctionBase):453 def __init__(self, *arg, **kw):454 raise NotImplementedError()455 456 class SetFunction(FunctionBase):457 def __init__(self, *arg, **kw):458 raise NotImplementedError()459 460 class ConditionBagFunction(BagFunction):461 def __init__(self, *arg, **kw):462 raise NotImplementedError()463 464 class ConditionSetFunction(FunctionBase):465 def __init__(self, *arg, **kw):466 raise NotImplementedError()467 468 class HigherOrderFunction(Function):469 def __init__(self, *arg, **kw):470 raise NotImplementedError()471 472 # TODO: Function classes  minimal implementation until opportunity to fully473 # implement474 class LogicalFunction(FunctionBase):475 476 def __init__(self, *arg, **kw):477 raise NotImplementedError()478 479 class NOfFunction(FunctionBase):480 481 def __init__(self, *arg, **kw):482 raise NotImplementedError()483 484 class NotFunction(FunctionBase):485 486 def __init__(self, *arg, **kw):487 raise NotImplementedError()488 489 class ComparisonFunction(FunctionBase):490 491 def __init__(self, *arg, **kw):492 raise NotImplementedError()493 494 class MatchFunction(FunctionBase):495 NAME_REGEXP_STRING_MATCH = \496 "urn:oasis:names:tc:xacml:1.0:function:regexpstringmatch"497 NAME_RFC822NAME_MATCH = \498 "urn:oasis:names:tc:xacml:1.0:function:rfc822Namematch"499 NAME_X500NAME_MATCH = \500 "urn:oasis:names:tc:xacml:1.0:function:x500Namematch"501 502 supportedIdentifiers = (NAME_REGEXP_STRING_MATCH,503 NAME_RFC822NAME_MATCH,504 NAME_X500NAME_MATCH)505 506 lut = {507 NAME_REGEXP_STRING_MATCH: 'regexpStringMatch',508 NAME_RFC822NAME_MATCH: 'rfc822NameMatch',509 NAME_X500NAME_MATCH: 'x500NameMatch'510 }511 512 def __init__(self, functionName, **kw):513 super(MatchFunction, self).__init__(functionName, **kw)514 515 def regexpStringMatch(self, regex, val):516 return re.match(regex, val) is not None517 518 def rfc822NameMatch(self, *inputs):519 raise NotImplementedError()520 521 def x500NameMatch(self, *inputs):522 raise NotImplementedError()523 524 def evaluate(self, inputs, context):525 matchFunction = getattr(self, MatchFunction.lut[self.functionName])526 match = matchFunction(self, *inputs)527 if match:528 return EvaluationResult(status=Status.STATUS_OK)529 530 531 class EqualFunction(FunctionBase):532 supportedIdentifiers = (533 "urn:oasis:names:tc:xacml:1.0:function:anyURIequal",534 "urn:oasis:names:tc:xacml:1.0:function:base64Binaryequal",535 "urn:oasis:names:tc:xacml:1.0:function:booleanequal",536 "urn:oasis:names:tc:xacml:1.0:function:dateequal",537 "urn:oasis:names:tc:xacml:1.0:function:dateTimeequal",538 "urn:oasis:names:tc:xacml:1.0:function:dayTimeDurationequal",539 "urn:oasis:names:tc:xacml:1.0:function:doubleequal",540 "urn:oasis:names:tc:xacml:1.0:function:hexBinaryequal",541 "urn:oasis:names:tc:xacml:1.0:function:integerequal",542 "urn:oasis:names:tc:xacml:1.0:function:rfc822Nameequal",543 "urn:oasis:names:tc:xacml:1.0:function:stringequal",544 "urn:oasis:names:tc:xacml:1.0:function:timeequal",545 "urn:oasis:names:tc:xacml:1.0:function:x500Nameequal",546 "urn:oasis:names:tc:xacml:1.0:function:yearMonthDurationequal"547 )548 549 (NAME_ANYURI_EQUAL,550 NAME_BASE64BINARY_EQUAL,551 NAME_BOOLEAN_EQUAL,552 NAME_DATE_EQUAL,553 NAME_DATETIME_EQUAL,554 NAME_DAYTIME_DURATION_EQUAL,555 NAME_DOUBLE_EQUAL,556 NAME_HEXBINARY_EQUAL,557 NAME_INTEGER_EQUAL,558 NAME_RFC822NAME_EQUAL,559 NAME_STRING_EQUAL,560 NAME_TIME_EQUAL,561 NAME_X500NAME_EQUAL,562 NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers563 564 lut = {565 NAME_STRING_EQUAL: 'stringEqual'566 }567 568 typeMap = {NAME_STRING_EQUAL: basestring}569 570 def __init__(self, functionName, **kw):571 super(EqualFunction, self).__init__(functionName, **kw)572 573 def evaluate(self, inputs, evaluationCtx):574 function = EqualFunction.lut.get(self.functionName)575 if function is None:576 if self.functionName in supportedIdentifiers:577 raise NotImplementedError("No implementation is available for "578 "%s" % self.functionName)579 else:580 raise AttributeError('function name "%s" not recognised '581 'for %s' % (self.functionName,582 self.__class__.__name__))583 584 return getattr(self, function)(inputs, evaluationCtx)585 586 def stringEqual(self, inputs, evaluationCtx):587 result = self.evalArgs(inputs, context, argValues)588 if result is not None:589 return result590 591 return EvaluationResult(argValues[0] == argValues[1])592 593 def getArgumentType(functionName):594 datatype = EqualFunction.typeMap.get(functionName);595 if datatype is None:596 raise AttributeError("Not a standard function: %s" % functionName)597 598 return datatype599 600 class AddFunction(FunctionBase):601 602 def __init__(self, *arg, **kw):603 raise NotImplementedError()604 605 class SubtractFunction(FunctionBase):606 607 def __init__(self, *arg, **kw):608 raise NotImplementedError()609 610 class MultiplyFunction(FunctionBase):611 612 def __init__(self, *arg, **kw):613 raise NotImplementedError()614 615 class DivideFunction(FunctionBase):616 617 def __init__(self, *arg, **kw):618 raise NotImplementedError()619 620 class ModFunction(FunctionBase):621 622 def __init__(self, *arg, **kw):623 raise NotImplementedError()624 625 class AbsFunction(FunctionBase):626 627 def __init__(self, *arg, **kw):628 raise NotImplementedError()629 630 class RoundFunction(FunctionBase):631 632 def __init__(self, *arg, **kw):633 raise NotImplementedError()634 635 class FloorFunction(FunctionBase):636 637 def __init__(self, *arg, **kw):638 raise NotImplementedError()639 640 class DateMathFunction(FunctionBase):641 642 def __init__(self, *arg, **kw):643 raise NotImplementedError()644 645 class GeneralBagFunction(BagFunction):646 647 def __init__(self, *arg, **kw):648 raise NotImplementedError()649 650 class NumericConvertFunction(FunctionBase):651 652 def __init__(self, *arg, **kw):653 raise NotImplementedError()654 655 class StringNormalizeFunction(FunctionBase):656 657 def __init__(self, *arg, **kw):658 raise NotImplementedError()659 660 class GeneralSetFunction(SetFunction):661 662 def __init__(self, *arg, **kw):663 raise NotImplementedError()664 665 class MapFunction(Function):666 supportedIdentifiers = ()667 NAME_MAP = FunctionBase.FUNCTION_NS + "map"668 669 def __init__(self, *arg, **kw):670 raise NotImplementedError()671 672 @classmethod673 def getInstance(cls, root):674 raise NotImplementedError()675 676 class FunctionProxy():677 678 def getInstance(self, root):679 raise NotImplementedError()680 681 class MapFunctionProxy(FunctionProxy):682 683 def getInstance(self, root):684 return MapFunction.getInstance(root)685 686 687 class UnknownIdentifierException(Exception):688 pass689 690 class FunctionTypeException(Exception):691 pass692 693 class ParsingException(Exception):694 pass695 696 class FunctionFactory(XacmlBase):697 '''Factory used to create all functions. There are three kinds of factories:698 general, condition, and target. These provide functions that can be used699 anywhere, only in a condition's root and only in a target (respectively).700 701 Note that all functions, except for abstract functions, are singletons, so702 any instance that is added to a factory will be the same one returned703 from the create methods. This is done because most functions don't have704 state, so there is no need to have more than one, or to spend the time705 creating multiple instances that all do the same thing.'''706 707 defaultFactoryProxy = StandardFunctionFactory()708 709 @classmethod710 def getTargetInstance(cls):711 '''Returns the default FunctionFactory that will only provide those712 functions that are usable in Target matching.713 714 @return a FunctionFactory for target functions'''715 return cls.defaultFactoryProxy.getTargetFactory()716 717 @classmethod718 def getConditionInstance(cls):719 '''Returns the default FunctionFactory that provides access to all the720 functions. These Functions are a superset of the Condition functions.721 722 @return a FunctionFactory for all functions723 '''724 return cls.defaultFactoryProxy.getConditionFactory()725 726 @classmethod727 def getGeneralInstance(cls):728 '''Sets the default factory. Note that this is just a place holder for729 now, and will be replaced with a more useful mechanism soon.'''730 return cls.defaultFactoryProxy.getGeneralFactory()731 732 733 def addFunction(self, function):734 '''Adds the function to the factory. Most functions have no state, so735 the singleton model used here is typically desirable. The factory will736 not enforce the requirement that a Target or Condition matching737 function must be boolean.738 739 @param function the Function to add to the factory740 '''741 raise NotImplementedError()742 743 def addAbstractFunction(self, functionProxy, identity):744 '''Adds the abstract function proxy to the factory. This is used for745 those functions which have state, or change behaviour (for instance746 the standard map function, which changes its return type based on747 how it is used).748 749 @param proxy the FunctionProxy to add to the factory750 @param identity the function's identifier751 '''752 raise NotImplementedError()753 754 def getSupportedFunctions(self):755 '''Returns the function identifiers supported by this factory.756 757 @return a Set of Strings'''758 raise NotImplementedError()759 760 def createFunction(self, identity):761 '''Tries to get an instance of the specified function.762 763 @param identity the name of the function764 '''765 raise NotImplementedError()766 767 def createAbstractFunction(self, identity, root):768 '''Tries to get an instance of the specified abstract function.769 770 @param identity the name of the function771 @param root the DOM root containing info used to create the function772 '''773 raise NotImplementedError()774 775 776 class FunctionFactoryProxy(XacmlBase):777 '''A simple proxy interface used to install new FunctionFactorys.778 The three kinds of factory (Target, Condition, and General) are tied779 together in this interface because implementors writing new factories780 should always implement all three types and provide them together'''781 def getTargetFactory():782 raise NotImplementedError()783 784 def getConditionFactory():785 raise NotImplementedError()786 787 def getGeneralFactory():788 raise NotImplementedError()789 790 791 class BasicFunctionFactoryProxy(FunctionFactoryProxy):792 '''A simple utility class that manages triples of function factories.'''793 794 # the triple of factories795 targetFactory = None796 conditionFactory = None797 generalFactory = None798 799 def __init__(targetFactory, conditionFactory, generalFactory):800 '''Creates a new proxy.801 802 @param targetFactory the target factory provided by this proxy803 @param conditionFactory the target condition provided by this proxy804 @param generalFactory the general factory provided by this proxy805 '''806 self.targetFactory = targetFactory807 self.conditionFactory = conditionFactory808 self.generalFactory = generalFactory809 810 def getTargetFactory():811 return targetFactory812 813 def getConditionFactory():814 return conditionFactory815 816 def getGeneralFactory():817 return generalFactory818 819 820 class BaseFunctionFactory(FunctionFactory):821 '''This is a basic implementation of <code>FunctionFactory</code>. It822 implements the insertion and retrieval methods, but it doesn't actually823 setup the factory with any functions. It also assumes a certain model824 with regard to the different kinds of functions (Target, Condition, and825 General). For this reason, you may want to reuse this class, or you826 may want to extend FunctionFactory directly, if you're writing a new827 factory implementation.828 829 Note that while this class is threadsafe on all creation methods, it830 is not safe to add support for a new function while creating an instance831 of a function. This follows from the assumption that most people will832 initialize these factories upfront, and then start processing without833 ever modifying the factories. If you need these mutual operations to834 be threadsafe, then you should write a wrapper class that implements835 the right synchronization.836 '''837 838 def __init__(self,839 superset=None,840 supportedFunctions=[],841 supportedAbstractFunctions={}):842 '''Sets a "superset factory". This is useful since843 the different function factories (Target, Condition, and General)844 have a superset relationship (Condition functions are a superset845 of Target functions, etc.). Adding a function to this factory will846 automatically add the same function to the superset factory.847 848 Constructor that defines the initial functions supported by this849 factory but doesn't use a superset factory.850 851 Constructor that defines the initial functions supported by this852 factory but doesn't use a superset factory.853 854 Constructor that defines the initial functions supported by this855 factory and uses a superset factory. Note that the functions856 supplied here are not propagated up to the superset factory, so857 you must either make sure the superset factory is correctly858 initialized or use BaseFunctionFactory(FunctionFactory)859 and then manually add each function.860 861 @param supportedFunctions a Set of Functions862 @param supportedAbstractFunctions a mapping from URI to863 FunctionProxy864 865 @param supportedFunctions a Set of Functions866 @param supportedAbstractFunctions a mapping from URI to FunctionProxy867 868 @param superset the superset factory or None'''869 870 # the backing maps for the Function objects871 self.functionMap = {}872 873 # the superset factory chained to this factory874 self.superset = superset875 876 for function in supportedFunctions:877 self.functionMap[function.functionId] = function878 879 for id in supportedAbstractFunctions.keys():880 proxy = supportedAbstractFunctions.get(id)881 self.functionMap[id] = proxy882 883 def addFunction(self, function):884 '''Adds the function to the factory. Most functions have no state, so885 the singleton model used here is typically desirable. The factory will886 not enforce the requirement that a Target or Condition matching887 function must be boolean.888 889 @param function the Function to add to the factory890 @raise TypeError if the function's identifier is already used or if the891 function is nonboolean (when this is a Target or Condition factory)892 '''893 id = function.functionId894 895 # make sure this doesn't already exist896 if id in self.functionMap:897 raise TypeError("function %s already exists" % id)898 899 # add to the superset factory900 if self.superset != None:901 self.superset.addFunction(function)902 903 # Add to this factory904 self.functionMap[id] = function905 906 907 def addAbstractFunction(self, proxy, id):908 '''Adds the abstract function proxy to the factory. This is used for909 those functions which have state, or change behaviour (for instance910 the standard map function, which changes its return type based on911 how it is used).912 913 @param proxy: the FunctionProxy to add to the factory914 @param id: the function's identifier915 916 @raise TypeError if the function's identifier is already used'''917 918 # make sure this doesn't already exist919 if id in self.functionMap:920 raise TypeError("function already exists")921 922 # add to the superset factory923 if self.superset != None:924 self.superset.addAbstractFunction(proxy, id)925 926 # finally, add to this factory927 functionMap[id] = proxy928 929 930 def getSupportedFunctions(self):931 '''Returns the function identifiers supported by this factory.932 933 @return a list of strings'''934 935 functions = self.functionMap.keys()936 937 if self.superset != None:938 functions += self.superset.getSupportedFunctions()939 940 return functions941 942 943 def createFunction(self, identity):944 '''Tries to get an instance of the specified function.945 946 @param identity the name of the function947 948 @throws UnknownIdentifierException if the name isn't known949 @throws FunctionTypeException if the name is known to map to an950 abstract function, and should therefore951 be created through createAbstractFunction952 '''953 entry = self.functionMap.get(identity)954 if entry is not None:955 if isinstance(entry, Function):956 return entry957 else:958 # this is actually a proxy, which means the other create959 # method should have been called960 raise FunctionTypeException("function is abstract")961 else:962 # we couldn't find a match963 raise UnknownIdentifierException("functions of type %s are not "964 "supported by this factory" %965 identity)966 967 968 def createAbstractFunction(identity, root):969 '''Tries to get an instance of the specified abstract function.970 971 @param identity the name of the function972 @param root the DOM root containing info used to create the function973 @param xpathVersion the version specified in the containing policy, or974 None if no version was specified975 976 @throws UnknownIdentifierException if the name isn't known977 @throws FunctionTypeException if the name is known to map to a978 concrete function, and should therefore979 be created through createFunction980 @throws ParsingException if the function can't be created with the981 given inputs'''982 983 entry = self.functionMap.get(identity)984 if entry is not None:985 if isinstance(entry, FunctionProxy):986 try:987 return entry.getInstance(root)988 989 except Exception, e:990 raise ParsingException("Couldn't create abstract function "991 "%s: %s" % identity, e)992 else:993 # this is actually a concrete function, which means that994 # the other create method should have been called995 raise FunctionTypeException("function is concrete")996 997 else:998 raise UnknownIdentifierException("Abstract functions of type %s "999 "are not supported by this "1000 "factory" % identity)1001 1002 1003 class StandardFunctionFactory(BaseFunctionFactory):1004 '''This factory supports the standard set of functions specified in XACML1005 1.0 and 1.1. It is the default factory used by the system, and imposes1006 a singleton pattern insuring that there is only ever one instance of1007 this class.1008 <p>1009 Note that because this supports only the standard functions, this1010 factory does not allow the addition of any other functions. If you call1011 addFunction on an instance of this class, an exception1012 will be thrown. If you need a standard factory that is modifiable,1013 you can either create a new BaseFunctionFactory (or some1014 other implementation of FunctionFactory) populated with1015 the standard functions from getStandardFunctions or1016 you can use getNewFactoryProxy to get a proxy containing1017 a new, modifiable set of factories.'''1018 1019 1020 # the three singleton instances1021 targetFactory = None1022 conditionFactory = None1023 generalFactory = None1024 1025 # the three function sets/maps that we use internally1026 targetFunctions = None1027 conditionFunctions = None1028 generalFunctions = None1029 1030 targetAbstractFunctions = None1031 conditionAbstractFunctions = None1032 generalAbstractFunctions = None1033 1034 # the set/map used by each singleton factory instance1035 supportedFunctions = None1036 supportedAbstractFunctions = None1037 1038 1039 def __init__(self, supportedFunctions, supportedAbstractFunctions):1040 '''Creates a new StandardFunctionFactory, making sure that the default1041 maps are initialized correctly. Standard factories can't be modified,1042 so there is no notion of supersetting since that's only used for1043 correctly propagating new functions.'''1044 super(StandardFunctionFactory, self).__init__(supportedFunctions,1045 supportedAbstractFunctions)1046 1047 self.supportedFunctions = supportedFunctions1048 self.supportedAbstractFunctions = supportedAbstractFunctions1049 1050 1051 1052 1053 def _initTargetFunctions(self):1054 '''Private initializer for the target functions. This is only ever1055 called once.'''1056 log.info("Initializing standard Target functions")1057 1058 # Emulate a list with unique items using a dict with only the keys set1059 StandardFunctionFactory.targetFunctions = {}1060 1061 # add EqualFunction1062 StandardFunctionFactory.targetFunctions.fromkeys(1063 EqualFunction.supportedIdentifiers)1064 1065 # add LogicalFunction1066 StandardFunctionFactory.targetFunctions.fromkeys(1067 LogicalFunction.supportedIdentifiers)1068 1069 # add NOfFunction1070 StandardFunctionFactory.targetFunctions.fromkeys(1071 NOfFunction.supportedIdentifiers)1072 1073 # add NotFunction1074 StandardFunctionFactory.targetFunctions.fromkeys(1075 NotFunction.supportedIdentifiers)1076 1077 # add ComparisonFunction1078 StandardFunctionFactory.targetFunctions.fromkeys(1079 ComparisonFunction.supportedIdentifiers)1080 1081 # add MatchFunction1082 StandardFunctionFactory.targetFunctions.fromkeys(1083 MatchFunction.supportedIdentifiers)1084 1085 StandardFunctionFactory.targetAbstractFunctions = {}1086 1087 1088 def _initConditionFunctions(self):1089 '''Private initializer for the condition functions. This is only ever1090 called once.'''1091 log.info("Initializing standard Condition functions")1092 1093 if StandardFunctionFactory.targetFunctions is None:1094 self._initTargetFunctions()1095 1096 StandardFunctionFactory.conditionFunctions = \1097 StandardFunctionFactory.targetFunctions.copy()1098 1099 # add condition functions from BagFunction1100 conditionFunctions.fromkeys(ConditionBagFunction.supportedIdentifiers)1101 1102 # add condition functions from SetFunction1103 conditionFunctions.fromkeys(ConditionSetFunction.supportedIdentifiers)1104 1105 # add condition functions from HigherOrderFunction1106 conditionFunctions.fromkeys(HigherOrderFunction.supportedIdentifiers)1107 1108 StandardFunctionFactory.conditionAbstractFunctions = \1109 StandardFunctionFactory.targetAbstractFunctions.copy()1110 1111 1112 def _initGeneralFunctions(self):1113 '''Private initializer for the general functions. This is only ever1114 called once.'''1115 1116 log.info("Initializing standard General functions")1117 1118 if StandardFunctionFactory.conditionFunctions is None:1119 self._initConditionFunctions()1120 1121 StandardFunctionFactory.generalFunctions = \1122 StandardFunctionFactory.conditionFunctions.copy()1123 1124 # add AddFunction1125 StandardFunctionFactory.generalFunctions.fromkeys(1126 AddFunction.supportedIdentifiers)1127 1128 # add SubtractFunction1129 StandardFunctionFactory.generalFunctions.fromkeys(1130 SubtractFunction.supportedIdentifiers)1131 1132 # add MultiplyFunction1133 StandardFunctionFactory.generalFunctions.fromkeys(1134 MultiplyFunction.supportedIdentifiers)1135 1136 # add DivideFunction1137 StandardFunctionFactory.generalFunctions.fromkeys(1138 DivideFunction.supportedIdentifiers)1139 1140 # add ModFunction1141 StandardFunctionFactory.generalFunctions.fromkeys(1142 ModFunction.supportedIdentifiers)1143 1144 # add AbsFunction1145 StandardFunctionFactory.generalFunctions.fromkeys(1146 AbsFunction.supportedIdentifiers)1147 1148 # add RoundFunction1149 StandardFunctionFactory.generalFunctions.fromkeys(1150 RoundFunction.supportedIdentifiers)1151 1152 # add FloorFunction1153 StandardFunctionFactory.generalFunctions.fromkeys(1154 FloorFunction.supportedIdentifiers)1155 1156 # add DateMathFunction1157 StandardFunctionFactory.generalFunctions.fromkeys(1158 DateMathFunction.supportedIdentifiers)1159 1160 # add general functions from BagFunction1161 StandardFunctionFactory.generalFunctions.fromkeys(1162 GeneralBagFunction.supportedIdentifiers)1163 1164 # add NumericConvertFunction1165 StandardFunctionFactory.generalFunctions.fromkeys(1166 NumericConvertFunction.supportedIdentifiers)1167 1168 # add StringNormalizeFunction1169 StandardFunctionFactory.generalFunctions.fromkeys(1170 StringNormalizeFunction.supportedIdentifiers)1171 1172 # add general functions from SetFunction1173 StandardFunctionFactory.generalFunctions.fromkeys(1174 GeneralSetFunction.supportedIdentifiers)1175 1176 StandardFunctionFactory.generalAbstractFunctions = \1177 StandardFunctionFactory.conditionAbstractFunctions.copy()1178 1179 # Add the map function's proxy1180 StandardFunctionFactory.generalAbstractFunctions[1181 MapFunction.NAME_MAP] = MapFunctionProxy()1182 1183 @classmethod1184 def getTargetFactory(cls):1185 '''Returns a FunctionFactory that will only provide those functions1186 that are usable in Target matching. This method enforces a singleton1187 model, meaning that this always returns the same instance, creating1188 the factory if it hasn't been requested before. This is the default1189 model used by the FunctionFactory, ensuring quick1190 access to this factory.1191 1192 @return a FunctionFactory for target functions'''1193 if StandardFunctionFactory.targetFactory is None:1194 if StandardFunctionFactory.targetFunctions is None:1195 StandardFunctionFactory._initTargetFunctions()1196 1197 if StandardFunctionFactory.targetFactory is None:1198 StandardFunctionFactory.targetFactory=StandardFunctionFactory(1199 StandardFunctionFactory.targetFunctions,1200 StandardFunctionFactory.targetAbstractFunctions)1201 1202 return StandardFunctionFactory.targetFactory1203 1204 1205 @classmethod1206 def getConditionFactory(cls):1207 '''Returns a FuntionFactory that will only provide those functions that1208 are usable in the root of the Condition. These Functions are a1209 superset of the Target functions. This method enforces a singleton1210 model, meaning that this always returns the same instance, creating1211 the factory if it hasn't been requested before. This is the default1212 model used by the FunctionFactory, ensuring quick1213 access to this factory.1214 1215 @return a FunctionFactory for condition functions1216 '''1217 if StandardFunctionFactory.conditionFactory is None:1218 if StandardFunctionFactory.conditionFunctions is None:1219 StandardFunctionFactory._initConditionFunctions()1220 1221 if StandardFunctionFactory.conditionFactory is None:1222 StandardFunctionFactory.conditionFactory = \1223 StandardFunctionFactory(1224 StandardFunctionFactory.conditionFunctions,1225 StandardFunctionFactory.conditionAbstractFunctions)1226 1227 return StandardFunctionFactory.conditionFactory1228 1229 1230 @classmethod1231 def getGeneralFactory(cls):1232 '''Returns a FunctionFactory that provides access to all the functions.1233 These Functions are a superset of the Condition functions. This method1234 enforces a singleton model, meaning that this always returns the same1235 instance, creating the factory if it hasn't been requested before.1236 This is the default model used by the FunctionFactory,1237 ensuring quick access to this factory.1238 1239 @return a FunctionFactory for all functions'''1240 1241 if StandardFunctionFactory.generalFactory is None:1242 if StandardFunctionFactory.generalFunctions is None:1243 StandardFunctionFactory._initGeneralFunctions()1244 1245 StandardFunctionFactory.generalFactory = \1246 StandardFunctionFactory(1247 StandardFunctionFactory.generalFunctions,1248 StandardFunctionFactory.generalAbstractFunctions)1249 1250 return StandardFunctionFactory.generalFactory1251 1252 1253 def getStandardFunctions(self):1254 '''Returns the set of functions that this standard factory supports.1255 1256 @return a Set of Functions'''1257 return tuple(self.supportedFunctions.keys())1258 1259 def getStandardAbstractFunctions(self):1260 '''Returns the set of abstract functions that this standard factory1261 supports as a mapping of identifier to proxy.1262 1263 @return a Map mapping URIs to FunctionProxys'''1264 return tuple(self.supportedAbstractFunctions.keys())1265 1266 1267 @classmethod1268 def getNewFactoryProxy(cls):1269 '''A convenience method that returns a proxy containing newly created1270 instances of BaseFunctionFactorys that are correctly1271 supersetted and contain the standard functions and abstract functions.1272 These factories allow adding support for new functions.1273 1274 @return a new proxy containing new factories supporting the standard1275 functions'''1276 1277 general = StandardFunctionFactory.getGeneralFactory()1278 1279 newGeneral=BaseFunctionFactory(general.getStandardFunctions(),1280 general.getStandardAbstractFunctions())1281 1282 condition = StandardFunctionFactory.getConditionFactory()1283 1284 newCondition = BaseFunctionFactory(newGeneral,1285 condition.getStandardFunctions(),1286 condition.getStandardAbstractFunctions())1287 1288 target = StandardFunctionFactory.getTargetFactory()1289 newTarget = BaseFunctionFactory(newCondition,1290 target.getStandardFunctions(),1291 target.getStandardAbstractFunctions())1292 1293 return BasicFunctionFactoryProxy(newTarget, newCondition, newGeneral)1294 1295 1296 1297 1298 def addFunction(self, function):1299 '''Always throws an exception, since support for new functions may not1300 be added to a standard factory.1301 1302 @param function the Function to add to the factory1303 @raise NotImplementedError'''1304 1305 raise NotImplementedError("a standard factory cannot support new "1306 "functions")1307 1308 1309 def addAbstractFunction(self, proxy, identity):1310 '''Always throws an exception, since support for new functions may not1311 be added to a standard factory.1312 1313 @param proxy the FunctionProxy to add to the factory1314 @param identity the function's identifier1315 1316 @raise NotImplementedError always'''1317 raise NotImplementedError("a standard factory cannot support new "1318 "functions")1319 1320 356 1321 357 class Status(XacmlBase): 
TI12security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/cond.py
r5159 r5162 1 """XACML cond module contains condition function classes and class factories 2 3 NERC DataGrid Project 4 """ 5 __author__ = "P J Kershaw" 6 __date__ = "02/04/09" 7 __copyright__ = "(C) 2009 Science and Technology Facilities Council" 8 __contact__ = "Philip.Kershaw@stfc.ac.uk" 9 __license__ = "BSD  see LICENSE file in toplevel directory" 10 __contact__ = "Philip.Kershaw@stfc.ac.uk" 11 __revision__ = "$Id$" 12 class FunctionBase(XacmlBase): 13 FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:" 14 15 def __init__(self, 16 functionName, 17 functionId=None, 18 paramType=None, 19 paramIsBag=False, 20 numParams=0, 21 minParams=0, 22 returnType='', 23 returnsBag=False): 24 25 self.functionName = functionName 26 self.functionId = functionId 27 self.returnType = None 28 self.returnsBag = False 29 30 self.singleType = True; 31 32 self.paramType = paramType 33 self.paramIsBag = paramIsBag 34 self.numParams = numParams 35 self.minParams = minParams 36 37 38 def _setFunctionName(self, functionName): 39 if functionName not in self.__class__.supportedIdentifiers: 40 functionList = ', '.join(self.__class__.supportedIdentifiers) 41 raise TypeError("Function name [%s] is not on of the recognised " 42 "types: %s" % (functionName, functionList)) 43 self._functionName = functionName 44 45 def _getFunctionName(self): 46 return getattr(self, '_functionName', None) 47 48 functionName = property(fset=_setFunctionName, 49 fget=_getFunctionName) 50 51 def checkInputs(self, inputs): 52 '''Checks that the given inputs are of the right types, in the right 53 order, and are the right number for this function to evaluate.''' 54 raise NotImplementedError() 55 56 def checkInputsNoBag(self, inputs): 57 '''Checks that the given inputs are of the right types, in the right 58 order, and are the right number for this function to evaluate.''' 59 raise NotImplementedError() 60 61 def evaluate(self, inputs, context): 62 '''Evaluates the Function using the given inputs.''' 63 raise NotImplementedError() 64 65 def evalArgs(self, params, context, args): 66 '''Evaluates each of the parameters, in order, filling in the argument 67 array with the resulting values. If any error occurs, this method 68 returns the error, otherwise null is returned, signalling that 69 evaluation was successful for all inputs, and the resulting argument 70 list can be used. 71 72 @param params a list of Evaluatable objects representing the parameters 73 to evaluate 74 @param context the representation of the request 75 @param args an array as long as the params list that will, on return, 76 contain the AttributeValues generated from evaluating all parameters 77 78 @return None if no errors were encountered, otherwise 79 an EvaluationResult representing the error 80 ''' 81 index = 0 82 83 for eval in params: 84 # get and evaluate the next parameter 85 result = eval.evaluate(context) 86 87 # If there was an error, pass it back... 88 if result.indeterminate(): 89 return result 90 91 # ...otherwise save it and keep going 92 args[index] = result.getAttributeValue() 93 index += 1 94 95 return None 96 97 # TODO: Condition classes  minimal implementation until opportunity to fully 98 # implement 99 class Function(XacmlBase): 100 def __init__(self, *arg, **kw): 101 raise NotImplementedError() 102 103 @classmethod 104 def getInstance(cls, root): 105 raise NotImplementedError() 106 107 class BagFunction(FunctionBase): 108 def __init__(self, *arg, **kw): 109 raise NotImplementedError() 110 111 class SetFunction(FunctionBase): 112 def __init__(self, *arg, **kw): 113 raise NotImplementedError() 114 115 class ConditionBagFunction(BagFunction): 116 def __init__(self, *arg, **kw): 117 raise NotImplementedError() 118 119 class ConditionSetFunction(FunctionBase): 120 def __init__(self, *arg, **kw): 121 raise NotImplementedError() 122 123 class HigherOrderFunction(Function): 124 def __init__(self, *arg, **kw): 125 raise NotImplementedError() 126 127 # TODO: Function classes  minimal implementation until opportunity to fully 128 # implement 129 class LogicalFunction(FunctionBase): 130 131 def __init__(self, *arg, **kw): 132 raise NotImplementedError() 133 134 class NOfFunction(FunctionBase): 135 136 def __init__(self, *arg, **kw): 137 raise NotImplementedError() 138 139 class NotFunction(FunctionBase): 140 141 def __init__(self, *arg, **kw): 142 raise NotImplementedError() 143 144 class ComparisonFunction(FunctionBase): 145 146 def __init__(self, *arg, **kw): 147 raise NotImplementedError() 148 149 class MatchFunction(FunctionBase): 150 NAME_REGEXP_STRING_MATCH = \ 151 "urn:oasis:names:tc:xacml:1.0:function:regexpstringmatch" 152 NAME_RFC822NAME_MATCH = \ 153 "urn:oasis:names:tc:xacml:1.0:function:rfc822Namematch" 154 NAME_X500NAME_MATCH = \ 155 "urn:oasis:names:tc:xacml:1.0:function:x500Namematch" 156 157 supportedIdentifiers = (NAME_REGEXP_STRING_MATCH, 158 NAME_RFC822NAME_MATCH, 159 NAME_X500NAME_MATCH) 160 161 lut = { 162 NAME_REGEXP_STRING_MATCH: 'regexpStringMatch', 163 NAME_RFC822NAME_MATCH: 'rfc822NameMatch', 164 NAME_X500NAME_MATCH: 'x500NameMatch' 165 } 166 167 def __init__(self, functionName, **kw): 168 super(MatchFunction, self).__init__(functionName, **kw) 169 170 def regexpStringMatch(self, regex, val): 171 return re.match(regex, val) is not None 172 173 def rfc822NameMatch(self, *inputs): 174 raise NotImplementedError() 175 176 def x500NameMatch(self, *inputs): 177 raise NotImplementedError() 178 179 def evaluate(self, inputs, context): 180 matchFunction = getattr(self, MatchFunction.lut[self.functionName]) 181 match = matchFunction(self, *inputs) 182 if match: 183 return EvaluationResult(status=Status.STATUS_OK) 184 185 186 class EqualFunction(FunctionBase): 187 supportedIdentifiers = ( 188 "urn:oasis:names:tc:xacml:1.0:function:anyURIequal", 189 "urn:oasis:names:tc:xacml:1.0:function:base64Binaryequal", 190 "urn:oasis:names:tc:xacml:1.0:function:booleanequal", 191 "urn:oasis:names:tc:xacml:1.0:function:dateequal", 192 "urn:oasis:names:tc:xacml:1.0:function:dateTimeequal", 193 "urn:oasis:names:tc:xacml:1.0:function:dayTimeDurationequal", 194 "urn:oasis:names:tc:xacml:1.0:function:doubleequal", 195 "urn:oasis:names:tc:xacml:1.0:function:hexBinaryequal", 196 "urn:oasis:names:tc:xacml:1.0:function:integerequal", 197 "urn:oasis:names:tc:xacml:1.0:function:rfc822Nameequal", 198 "urn:oasis:names:tc:xacml:1.0:function:stringequal", 199 "urn:oasis:names:tc:xacml:1.0:function:timeequal", 200 "urn:oasis:names:tc:xacml:1.0:function:x500Nameequal", 201 "urn:oasis:names:tc:xacml:1.0:function:yearMonthDurationequal" 202 ) 203 204 (NAME_ANYURI_EQUAL, 205 NAME_BASE64BINARY_EQUAL, 206 NAME_BOOLEAN_EQUAL, 207 NAME_DATE_EQUAL, 208 NAME_DATETIME_EQUAL, 209 NAME_DAYTIME_DURATION_EQUAL, 210 NAME_DOUBLE_EQUAL, 211 NAME_HEXBINARY_EQUAL, 212 NAME_INTEGER_EQUAL, 213 NAME_RFC822NAME_EQUAL, 214 NAME_STRING_EQUAL, 215 NAME_TIME_EQUAL, 216 NAME_X500NAME_EQUAL, 217 NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers 218 219 lut = { 220 NAME_STRING_EQUAL: 'stringEqual' 221 } 222 223 typeMap = {NAME_STRING_EQUAL: basestring} 224 225 def __init__(self, functionName, **kw): 226 super(EqualFunction, self).__init__(functionName, **kw) 227 228 def evaluate(self, inputs, evaluationCtx): 229 function = EqualFunction.lut.get(self.functionName) 230 if function is None: 231 if self.functionName in supportedIdentifiers: 232 raise NotImplementedError("No implementation is available for " 233 "%s" % self.functionName) 234 else: 235 raise AttributeError('function name "%s" not recognised ' 236 'for %s' % (self.functionName, 237 self.__class__.__name__)) 238 239 return getattr(self, function)(inputs, evaluationCtx) 240 241 def stringEqual(self, inputs, evaluationCtx): 242 result = self.evalArgs(inputs, context, argValues) 243 if result is not None: 244 return result 245 246 return EvaluationResult(argValues[0] == argValues[1]) 247 248 def getArgumentType(functionName): 249 datatype = EqualFunction.typeMap.get(functionName); 250 if datatype is None: 251 raise AttributeError("Not a standard function: %s" % functionName) 252 253 return datatype 254 255 class AddFunction(FunctionBase): 256 257 def __init__(self, *arg, **kw): 258 raise NotImplementedError() 259 260 class SubtractFunction(FunctionBase): 261 262 def __init__(self, *arg, **kw): 263 raise NotImplementedError() 264 265 class MultiplyFunction(FunctionBase): 266 267 def __init__(self, *arg, **kw): 268 raise NotImplementedError() 269 270 class DivideFunction(FunctionBase): 271 272 def __init__(self, *arg, **kw): 273 raise NotImplementedError() 274 275 class ModFunction(FunctionBase): 276 277 def __init__(self, *arg, **kw): 278 raise NotImplementedError() 279 280 class AbsFunction(FunctionBase): 281 282 def __init__(self, *arg, **kw): 283 raise NotImplementedError() 284 285 class RoundFunction(FunctionBase): 286 287 def __init__(self, *arg, **kw): 288 raise NotImplementedError() 289 290 class FloorFunction(FunctionBase): 291 292 def __init__(self, *arg, **kw): 293 raise NotImplementedError() 294 295 class DateMathFunction(FunctionBase): 296 297 def __init__(self, *arg, **kw): 298 raise NotImplementedError() 299 300 class GeneralBagFunction(BagFunction): 301 302 def __init__(self, *arg, **kw): 303 raise NotImplementedError() 304 305 class NumericConvertFunction(FunctionBase): 306 307 def __init__(self, *arg, **kw): 308 raise NotImplementedError() 309 310 class StringNormalizeFunction(FunctionBase): 311 312 def __init__(self, *arg, **kw): 313 raise NotImplementedError() 314 315 class GeneralSetFunction(SetFunction): 316 317 def __init__(self, *arg, **kw): 318 raise NotImplementedError() 319 320 class MapFunction(Function): 321 supportedIdentifiers = () 322 NAME_MAP = FunctionBase.FUNCTION_NS + "map" 323 324 def __init__(self, *arg, **kw): 325 raise NotImplementedError() 326 327 @classmethod 328 def getInstance(cls, root): 329 raise NotImplementedError() 330 331 class FunctionProxy(): 332 333 def getInstance(self, root): 334 raise NotImplementedError() 335 336 class MapFunctionProxy(FunctionProxy): 337 338 def getInstance(self, root): 339 return MapFunction.getInstance(root) 340 341 342 class UnknownIdentifierException(Exception): 343 pass 344 345 class FunctionTypeException(Exception): 346 pass 347 348 class ParsingException(Exception): 349 pass 350 351 class FunctionFactory(XacmlBase): 352 '''Factory used to create all functions. There are three kinds of factories: 353 general, condition, and target. These provide functions that can be used 354 anywhere, only in a condition's root and only in a target (respectively). 355 356 Note that all functions, except for abstract functions, are singletons, so 357 any instance that is added to a factory will be the same one returned 358 from the create methods. This is done because most functions don't have 359 state, so there is no need to have more than one, or to spend the time 360 creating multiple instances that all do the same thing.''' 361 362 defaultFactoryProxy = StandardFunctionFactory() 363 364 @classmethod 365 def getTargetInstance(cls): 366 '''Returns the default FunctionFactory that will only provide those 367 functions that are usable in Target matching. 368 369 @return a FunctionFactory for target functions''' 370 return cls.defaultFactoryProxy.getTargetFactory() 371 372 @classmethod 373 def getConditionInstance(cls): 374 '''Returns the default FunctionFactory that provides access to all the 375 functions. These Functions are a superset of the Condition functions. 376 377 @return a FunctionFactory for all functions 378 ''' 379 return cls.defaultFactoryProxy.getConditionFactory() 380 381 @classmethod 382 def getGeneralInstance(cls): 383 '''Sets the default factory. Note that this is just a place holder for 384 now, and will be replaced with a more useful mechanism soon.''' 385 return cls.defaultFactoryProxy.getGeneralFactory() 386 387 388 def addFunction(self, function): 389 '''Adds the function to the factory. Most functions have no state, so 390 the singleton model used here is typically desirable. The factory will 391 not enforce the requirement that a Target or Condition matching 392 function must be boolean. 393 394 @param function the Function to add to the factory 395 ''' 396 raise NotImplementedError() 397 398 def addAbstractFunction(self, functionProxy, identity): 399 '''Adds the abstract function proxy to the factory. This is used for 400 those functions which have state, or change behaviour (for instance 401 the standard map function, which changes its return type based on 402 how it is used). 403 404 @param proxy the FunctionProxy to add to the factory 405 @param identity the function's identifier 406 ''' 407 raise NotImplementedError() 408 409 def getSupportedFunctions(self): 410 '''Returns the function identifiers supported by this factory. 411 412 @return a Set of Strings''' 413 raise NotImplementedError() 414 415 def createFunction(self, identity): 416 '''Tries to get an instance of the specified function. 417 418 @param identity the name of the function 419 ''' 420 raise NotImplementedError() 421 422 def createAbstractFunction(self, identity, root): 423 '''Tries to get an instance of the specified abstract function. 424 425 @param identity the name of the function 426 @param root the DOM root containing info used to create the function 427 ''' 428 raise NotImplementedError() 429 430 431 class FunctionFactoryProxy(XacmlBase): 432 '''A simple proxy interface used to install new FunctionFactorys. 433 The three kinds of factory (Target, Condition, and General) are tied 434 together in this interface because implementors writing new factories 435 should always implement all three types and provide them together''' 436 def getTargetFactory(): 437 raise NotImplementedError() 438 439 def getConditionFactory(): 440 raise NotImplementedError() 441 442 def getGeneralFactory(): 443 raise NotImplementedError() 444 445 446 class BasicFunctionFactoryProxy(FunctionFactoryProxy): 447 '''A simple utility class that manages triples of function factories.''' 448 449 # the triple of factories 450 targetFactory = None 451 conditionFactory = None 452 generalFactory = None 453 454 def __init__(targetFactory, conditionFactory, generalFactory): 455 '''Creates a new proxy. 456 457 @param targetFactory the target factory provided by this proxy 458 @param conditionFactory the target condition provided by this proxy 459 @param generalFactory the general factory provided by this proxy 460 ''' 461 self.targetFactory = targetFactory 462 self.conditionFactory = conditionFactory 463 self.generalFactory = generalFactory 464 465 def getTargetFactory(): 466 return targetFactory 467 468 def getConditionFactory(): 469 return conditionFactory 470 471 def getGeneralFactory(): 472 return generalFactory 473 474 475 class BaseFunctionFactory(FunctionFactory): 476 '''This is a basic implementation of <code>FunctionFactory</code>. It 477 implements the insertion and retrieval methods, but it doesn't actually 478 setup the factory with any functions. It also assumes a certain model 479 with regard to the different kinds of functions (Target, Condition, and 480 General). For this reason, you may want to reuse this class, or you 481 may want to extend FunctionFactory directly, if you're writing a new 482 factory implementation. 483 484 Note that while this class is threadsafe on all creation methods, it 485 is not safe to add support for a new function while creating an instance 486 of a function. This follows from the assumption that most people will 487 initialize these factories upfront, and then start processing without 488 ever modifying the factories. If you need these mutual operations to 489 be threadsafe, then you should write a wrapper class that implements 490 the right synchronization. 491 ''' 492 493 def __init__(self, 494 superset=None, 495 supportedFunctions=[], 496 supportedAbstractFunctions={}): 497 '''Sets a "superset factory". This is useful since 498 the different function factories (Target, Condition, and General) 499 have a superset relationship (Condition functions are a superset 500 of Target functions, etc.). Adding a function to this factory will 501 automatically add the same function to the superset factory. 502 503 Constructor that defines the initial functions supported by this 504 factory but doesn't use a superset factory. 505 506 Constructor that defines the initial functions supported by this 507 factory but doesn't use a superset factory. 508 509 Constructor that defines the initial functions supported by this 510 factory and uses a superset factory. Note that the functions 511 supplied here are not propagated up to the superset factory, so 512 you must either make sure the superset factory is correctly 513 initialized or use BaseFunctionFactory(FunctionFactory) 514 and then manually add each function. 515 516 @param supportedFunctions a Set of Functions 517 @param supportedAbstractFunctions a mapping from URI to 518 FunctionProxy 519 520 @param supportedFunctions a Set of Functions 521 @param supportedAbstractFunctions a mapping from URI to FunctionProxy 522 523 @param superset the superset factory or None''' 524 525 # the backing maps for the Function objects 526 self.functionMap = {} 527 528 # the superset factory chained to this factory 529 self.superset = superset 530 531 for function in supportedFunctions: 532 self.functionMap[function.functionId] = function 533 534 for id in supportedAbstractFunctions.keys(): 535 proxy = supportedAbstractFunctions.get(id) 536 self.functionMap[id] = proxy 537 538 def addFunction(self, function): 539 '''Adds the function to the factory. Most functions have no state, so 540 the singleton model used here is typically desirable. The factory will 541 not enforce the requirement that a Target or Condition matching 542 function must be boolean. 543 544 @param function the Function to add to the factory 545 @raise TypeError if the function's identifier is already used or if the 546 function is nonboolean (when this is a Target or Condition factory) 547 ''' 548 id = function.functionId 549 550 # make sure this doesn't already exist 551 if id in self.functionMap: 552 raise TypeError("function %s already exists" % id) 553 554 # add to the superset factory 555 if self.superset != None: 556 self.superset.addFunction(function) 557 558 # Add to this factory 559 self.functionMap[id] = function 560 561 562 def addAbstractFunction(self, proxy, id): 563 '''Adds the abstract function proxy to the factory. This is used for 564 those functions which have state, or change behaviour (for instance 565 the standard map function, which changes its return type based on 566 how it is used). 567 568 @param proxy: the FunctionProxy to add to the factory 569 @param id: the function's identifier 570 571 @raise TypeError if the function's identifier is already used''' 572 573 # make sure this doesn't already exist 574 if id in self.functionMap: 575 raise TypeError("function already exists") 576 577 # add to the superset factory 578 if self.superset != None: 579 self.superset.addAbstractFunction(proxy, id) 580 581 # finally, add to this factory 582 functionMap[id] = proxy 583 584 585 def getSupportedFunctions(self): 586 '''Returns the function identifiers supported by this factory. 587 588 @return a list of strings''' 589 590 functions = self.functionMap.keys() 591 592 if self.superset != None: 593 functions += self.superset.getSupportedFunctions() 594 595 return functions 596 597 598 def createFunction(self, identity): 599 '''Tries to get an instance of the specified function. 600 601 @param identity the name of the function 602 603 @throws UnknownIdentifierException if the name isn't known 604 @throws FunctionTypeException if the name is known to map to an 605 abstract function, and should therefore 606 be created through createAbstractFunction 607 ''' 608 entry = self.functionMap.get(identity) 609 if entry is not None: 610 if isinstance(entry, Function): 611 return entry 612 else: 613 # this is actually a proxy, which means the other create 614 # method should have been called 615 raise FunctionTypeException("function is abstract") 616 else: 617 # we couldn't find a match 618 raise UnknownIdentifierException("functions of type %s are not " 619 "supported by this factory" % 620 identity) 621 622 623 def createAbstractFunction(identity, root): 624 '''Tries to get an instance of the specified abstract function. 625 626 @param identity the name of the function 627 @param root the DOM root containing info used to create the function 628 @param xpathVersion the version specified in the containing policy, or 629 None if no version was specified 630 631 @throws UnknownIdentifierException if the name isn't known 632 @throws FunctionTypeException if the name is known to map to a 633 concrete function, and should therefore 634 be created through createFunction 635 @throws ParsingException if the function can't be created with the 636 given inputs''' 637 638 entry = self.functionMap.get(identity) 639 if entry is not None: 640 if isinstance(entry, FunctionProxy): 641 try: 642 return entry.getInstance(root) 643 644 except Exception, e: 645 raise ParsingException("Couldn't create abstract function " 646 "%s: %s" % identity, e) 647 else: 648 # this is actually a concrete function, which means that 649 # the other create method should have been called 650 raise FunctionTypeException("function is concrete") 651 652 else: 653 raise UnknownIdentifierException("Abstract functions of type %s " 654 "are not supported by this " 655 "factory" % identity) 656 657 658 class StandardFunctionFactory(BaseFunctionFactory): 659 '''This factory supports the standard set of functions specified in XACML 660 1.0 and 1.1. It is the default factory used by the system, and imposes 661 a singleton pattern insuring that there is only ever one instance of 662 this class. 663 <p> 664 Note that because this supports only the standard functions, this 665 factory does not allow the addition of any other functions. If you call 666 addFunction on an instance of this class, an exception 667 will be thrown. If you need a standard factory that is modifiable, 668 you can either create a new BaseFunctionFactory (or some 669 other implementation of FunctionFactory) populated with 670 the standard functions from getStandardFunctions or 671 you can use getNewFactoryProxy to get a proxy containing 672 a new, modifiable set of factories.''' 673 674 675 # the three singleton instances 676 targetFactory = None 677 conditionFactory = None 678 generalFactory = None 679 680 # the three function sets/maps that we use internally 681 targetFunctions = None 682 conditionFunctions = None 683 generalFunctions = None 684 685 targetAbstractFunctions = None 686 conditionAbstractFunctions = None 687 generalAbstractFunctions = None 688 689 # the set/map used by each singleton factory instance 690 supportedFunctions = None 691 supportedAbstractFunctions = None 692 693 694 def __init__(self, supportedFunctions, supportedAbstractFunctions): 695 '''Creates a new StandardFunctionFactory, making sure that the default 696 maps are initialized correctly. Standard factories can't be modified, 697 so there is no notion of supersetting since that's only used for 698 correctly propagating new functions.''' 699 super(StandardFunctionFactory, self).__init__(supportedFunctions, 700 supportedAbstractFunctions) 701 702 self.supportedFunctions = supportedFunctions 703 self.supportedAbstractFunctions = supportedAbstractFunctions 704 705 706 707 708 def _initTargetFunctions(self): 709 '''Private initializer for the target functions. This is only ever 710 called once.''' 711 log.info("Initializing standard Target functions") 712 713 # Emulate a list with unique items using a dict with only the keys set 714 StandardFunctionFactory.targetFunctions = {} 715 716 # add EqualFunction 717 StandardFunctionFactory.targetFunctions.fromkeys( 718 EqualFunction.supportedIdentifiers) 719 720 # add LogicalFunction 721 StandardFunctionFactory.targetFunctions.fromkeys( 722 LogicalFunction.supportedIdentifiers) 723 724 # add NOfFunction 725 StandardFunctionFactory.targetFunctions.fromkeys( 726 NOfFunction.supportedIdentifiers) 727 728 # add NotFunction 729 StandardFunctionFactory.targetFunctions.fromkeys( 730 NotFunction.supportedIdentifiers) 731 732 # add ComparisonFunction 733 StandardFunctionFactory.targetFunctions.fromkeys( 734 ComparisonFunction.supportedIdentifiers) 735 736 # add MatchFunction 737 StandardFunctionFactory.targetFunctions.fromkeys( 738 MatchFunction.supportedIdentifiers) 739 740 StandardFunctionFactory.targetAbstractFunctions = {} 741 742 743 def _initConditionFunctions(self): 744 '''Private initializer for the condition functions. This is only ever 745 called once.''' 746 log.info("Initializing standard Condition functions") 747 748 if StandardFunctionFactory.targetFunctions is None: 749 self._initTargetFunctions() 750 751 StandardFunctionFactory.conditionFunctions = \ 752 StandardFunctionFactory.targetFunctions.copy() 753 754 # add condition functions from BagFunction 755 conditionFunctions.fromkeys(ConditionBagFunction.supportedIdentifiers) 756 757 # add condition functions from SetFunction 758 conditionFunctions.fromkeys(ConditionSetFunction.supportedIdentifiers) 759 760 # add condition functions from HigherOrderFunction 761 conditionFunctions.fromkeys(HigherOrderFunction.supportedIdentifiers) 762 763 StandardFunctionFactory.conditionAbstractFunctions = \ 764 StandardFunctionFactory.targetAbstractFunctions.copy() 765 766 767 def _initGeneralFunctions(self): 768 '''Private initializer for the general functions. This is only ever 769 called once.''' 770 771 log.info("Initializing standard General functions") 772 773 if StandardFunctionFactory.conditionFunctions is None: 774 self._initConditionFunctions() 775 776 StandardFunctionFactory.generalFunctions = \ 777 StandardFunctionFactory.conditionFunctions.copy() 778 779 # add AddFunction 780 StandardFunctionFactory.generalFunctions.fromkeys( 781 AddFunction.supportedIdentifiers) 782 783 # add SubtractFunction 784 StandardFunctionFactory.generalFunctions.fromkeys( 785 SubtractFunction.supportedIdentifiers) 786 787 # add MultiplyFunction 788 StandardFunctionFactory.generalFunctions.fromkeys( 789 MultiplyFunction.supportedIdentifiers) 790 791 # add DivideFunction 792 StandardFunctionFactory.generalFunctions.fromkeys( 793 DivideFunction.supportedIdentifiers) 794 795 # add ModFunction 796 StandardFunctionFactory.generalFunctions.fromkeys( 797 ModFunction.supportedIdentifiers) 798 799 # add AbsFunction 800 StandardFunctionFactory.generalFunctions.fromkeys( 801 AbsFunction.supportedIdentifiers) 802 803 # add RoundFunction 804 StandardFunctionFactory.generalFunctions.fromkeys( 805 RoundFunction.supportedIdentifiers) 806 807 # add FloorFunction 808 StandardFunctionFactory.generalFunctions.fromkeys( 809 FloorFunction.supportedIdentifiers) 810 811 # add DateMathFunction 812 StandardFunctionFactory.generalFunctions.fromkeys( 813 DateMathFunction.supportedIdentifiers) 814 815 # add general functions from BagFunction 816 StandardFunctionFactory.generalFunctions.fromkeys( 817 GeneralBagFunction.supportedIdentifiers) 818 819 # add NumericConvertFunction 820 StandardFunctionFactory.generalFunctions.fromkeys( 821 NumericConvertFunction.supportedIdentifiers) 822 823 # add StringNormalizeFunction 824 StandardFunctionFactory.generalFunctions.fromkeys( 825 StringNormalizeFunction.supportedIdentifiers) 826 827 # add general functions from SetFunction 828 StandardFunctionFactory.generalFunctions.fromkeys( 829 GeneralSetFunction.supportedIdentifiers) 830 831 StandardFunctionFactory.generalAbstractFunctions = \ 832 StandardFunctionFactory.conditionAbstractFunctions.copy() 833 834 # Add the map function's proxy 835 StandardFunctionFactory.generalAbstractFunctions[ 836 MapFunction.NAME_MAP] = MapFunctionProxy() 837 838 @classmethod 839 def getTargetFactory(cls): 840 '''Returns a FunctionFactory that will only provide those functions 841 that are usable in Target matching. This method enforces a singleton 842 model, meaning that this always returns the same instance, creating 843 the factory if it hasn't been requested before. This is the default 844 model used by the FunctionFactory, ensuring quick 845 access to this factory. 846 847 @return a FunctionFactory for target functions''' 848 if StandardFunctionFactory.targetFactory is None: 849 if StandardFunctionFactory.targetFunctions is None: 850 StandardFunctionFactory._initTargetFunctions() 851 852 if StandardFunctionFactory.targetFactory is None: 853 StandardFunctionFactory.targetFactory=StandardFunctionFactory( 854 StandardFunctionFactory.targetFunctions, 855 StandardFunctionFactory.targetAbstractFunctions) 856 857 return StandardFunctionFactory.targetFactory 858 859 860 @classmethod 861 def getConditionFactory(cls): 862 '''Returns a FuntionFactory that will only provide those functions that 863 are usable in the root of the Condition. These Functions are a 864 superset of the Target functions. This method enforces a singleton 865 model, meaning that this always returns the same instance, creating 866 the factory if it hasn't been requested before. This is the default 867 model used by the FunctionFactory, ensuring quick 868 access to this factory. 869 870 @return a FunctionFactory for condition functions 871 ''' 872 if StandardFunctionFactory.conditionFactory is None: 873 if StandardFunctionFactory.conditionFunctions is None: 874 StandardFunctionFactory._initConditionFunctions() 875 876 if StandardFunctionFactory.conditionFactory is None: 877 StandardFunctionFactory.conditionFactory = \ 878 StandardFunctionFactory( 879 StandardFunctionFactory.conditionFunctions, 880 StandardFunctionFactory.conditionAbstractFunctions) 881 882 return StandardFunctionFactory.conditionFactory 883 884 885 @classmethod 886 def getGeneralFactory(cls): 887 '''Returns a FunctionFactory that provides access to all the functions. 888 These Functions are a superset of the Condition functions. This method 889 enforces a singleton model, meaning that this always returns the same 890 instance, creating the factory if it hasn't been requested before. 891 This is the default model used by the FunctionFactory, 892 ensuring quick access to this factory. 893 894 @return a FunctionFactory for all functions''' 895 896 if StandardFunctionFactory.generalFactory is None: 897 if StandardFunctionFactory.generalFunctions is None: 898 StandardFunctionFactory._initGeneralFunctions() 899 900 StandardFunctionFactory.generalFactory = \ 901 StandardFunctionFactory( 902 StandardFunctionFactory.generalFunctions, 903 StandardFunctionFactory.generalAbstractFunctions) 904 905 return StandardFunctionFactory.generalFactory 906 907 908 def getStandardFunctions(self): 909 '''Returns the set of functions that this standard factory supports. 910 911 @return a Set of Functions''' 912 return tuple(self.supportedFunctions.keys()) 913 914 def getStandardAbstractFunctions(self): 915 '''Returns the set of abstract functions that this standard factory 916 supports as a mapping of identifier to proxy. 917 918 @return a Map mapping URIs to FunctionProxys''' 919 return tuple(self.supportedAbstractFunctions.keys()) 920 921 922 @classmethod 923 def getNewFactoryProxy(cls): 924 '''A convenience method that returns a proxy containing newly created 925 instances of BaseFunctionFactorys that are correctly 926 supersetted and contain the standard functions and abstract functions. 927 These factories allow adding support for new functions. 928 929 @return a new proxy containing new factories supporting the standard 930 functions''' 931 932 general = StandardFunctionFactory.getGeneralFactory() 933 934 newGeneral=BaseFunctionFactory(general.getStandardFunctions(), 935 general.getStandardAbstractFunctions()) 936 937 condition = StandardFunctionFactory.getConditionFactory() 938 939 newCondition = BaseFunctionFactory(newGeneral, 940 condition.getStandardFunctions(), 941 condition.getStandardAbstractFunctions()) 942 943 target = StandardFunctionFactory.getTargetFactory() 944 newTarget = BaseFunctionFactory(newCondition, 945 target.getStandardFunctions(), 946 target.getStandardAbstractFunctions()) 947 948 return BasicFunctionFactoryProxy(newTarget, newCondition, newGeneral) 949 950 951 952 953 def addFunction(self, function): 954 '''Always throws an exception, since support for new functions may not 955 be added to a standard factory. 956 957 @param function the Function to add to the factory 958 @raise NotImplementedError''' 959 960 raise NotImplementedError("a standard factory cannot support new " 961 "functions") 962 963 964 def addAbstractFunction(self, proxy, identity): 965 '''Always throws an exception, since support for new functions may not 966 be added to a standard factory. 967 968 @param proxy the FunctionProxy to add to the factory 969 @param identity the function's identifier 970 971 @raise NotImplementedError always''' 972 raise NotImplementedError("a standard factory cannot support new " 973 "functions")
Note: See TracChangeset
for help on using the changeset viewer.