def processPath(path, invoker, encoder, values=None): ''' Process the gateway path marking groups for all property types present in the path. @param path: Path The path to process as a gateway pattern. @param invoker: Invoker The invoker to process the pattern based on. @param encoder: IEncoderPath The path encoder used for processing to path. @param values: dictionary{TypeProperty: string} The static values to be placed on the path, as a key the type property that has the value. @return: tuple(string, list[TypeProperty]) Returns the gateway path and the property types that the path has markers for. ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(encoder, IEncoderPath), 'Invalid path encoder %s' % encoder replaceMarkers, types = [], [] for propertyType in propertyTypesOf(path, invoker): assert isinstance(propertyType, TypeProperty), 'Invalid property type %s' % propertyType if values: assert isinstance(values, dict), 'Invalid values %s' % values value = values.get(propertyType) if value is not None: assert isinstance(value, str), 'Invalid value %s' % value replaceMarkers.append(value) continue replaceMarkers.append('{%s}' % len(types)) types.append(propertyType) return encoder.encode(path, invalid=ReplacerWithMarkers().register(replaceMarkers)), types
def process(self, node, invoker): ''' Process the structure for the provided node and invoker. @param node: Node The node to process for. @param invoker: Invoker The invoker to process. @return: tuple(list[TypeProperty], @see: StructNode.process)|None The list of path property types. ''' assert isinstance(node, Node), 'Invalid node %s' % node structNode = self.structNodes.get(node) if structNode is None: structNode = self.structNodes[node] = StructNode() assert isinstance(structNode, StructNode) data = structNode.process(invoker) if data is None: return typesPath = self.typesPaths.get(node) if typesPath is None: typesPath = self.typesPaths[node] = (propertyTypesOf( node, invoker), ) return typesPath + data
def _processPatternWithSecured(self, path, invoker, replacer): ''' Process the access pattern for secured by using the path and replacer. @param path: Path The path to process the pattern based on. @param invoker: Invoker The invoker to process the pattern based on. @param replacer: PatternReplacer The replacer to use on the path. @return: string It returns the pattern ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(replacer, ReplacerWithMarkers), 'Invalid replacer %s' % replacer replaceMarkers = [] for propertyType in propertyTypesOf(path, invoker): assert isinstance(propertyType, TypeProperty), 'Invalid property type %s' % propertyType if propertyType.isOf(int): replaceMarkers.append('[0-9\\-]+') elif propertyType.isOf(str): replaceMarkers.append('[^\\/]+') else: raise Exception('Unusable type \'%s\'' % propertyType) replacer.register(replaceMarkers) pattern = ''.join(('\\/'.join(path.toPaths(self.converterPath, replacer)), '[\\/]?(?:\\.|$)')) return pattern
def processFilter(self, rfilter, provider, marker, encoder): ''' Process the provided filter. @param rfilter: Filter The resource filter to process. @param provider: callable|None The callable used in solving the authenticated values. @param marker: string The resource marker to place in the filter path, this marker is used to identify the group in the gateway pattern. @param encoder: IEncoderPath The encoder path to be used for the gateways resource paths and patterns. @return: string|None The marked filter path, None if the filter is invalid. ''' assert isinstance(rfilter, Filter), 'Invalid filter %s' % rfilter assert isinstance(rfilter.filter, IAclFilter), 'Invalid filter %s of %s' % (rfilter.filter, rfilter) typeService = typeFor(rfilter.filter) assert isinstance(typeService, TypeService), 'Invalid filter %s, is not a REST service' % rfilter.filter assert isinstance(marker, str), 'Invalid marker %s' % marker assert isinstance(encoder, IEncoderPath), 'Invalid encoder path %s' % encoder path = self._cacheFilters.get(typeService) if not path: nodes = findNodesFor(self.resourcesRoot, typeService, 'isAllowed') if not nodes: log.error('The filter service %s cannot be located in the resources tree', typeService) return if len(nodes) > 1: log.error('To many nodes for service %s in the resources tree, don\'t know which one to use', typeService) return node = nodes[0] assert isinstance(node, Node) path = pathForNode(node) if __debug__: # Just checking that the properties are ok propertyTypes = propertyTypesOf(path, node.get) assert len(propertyTypes) == 2, 'Invalid path %s for filter' % path indexAuth = propertyTypes.index(rfilter.authenticated) assert indexAuth >= 0, 'Invalid authenticated %s for path %s' % (rfilter.authenticated, path) indexRsc = propertyTypes.index(rfilter.resource) assert indexRsc >= 0, 'Invalid resource %s for path %s' % (rfilter.resource, path) assert indexAuth < indexRsc, 'Invalid path %s, improper order for types' % path assert isinstance(path, Path), 'Invalid path %s' % path if provider: assert callable(provider), 'Invalid authenticated provider %s' % provider valueAuth = provider(rfilter.authenticated) else: valueAuth = None if valueAuth is None: log.error('The filter service %s has not authenticated value for %s', typeService, rfilter.authenticated) return return encoder.encode(path, invalid=ReplacerWithMarkers().register((valueAuth, marker)), quoted=False)
def _processFilters(self, filters, replacer): ''' Process the filters into path filters. @param filters: Iterable(Filter) The filters to process. @param replacer: PatternReplacer The replacer to use on the path. @return: dictionary{TypeProperty, tuple(string, dictionary{TypeProperty: string}} A dictionary containing {resource type, (marked path, {authenticated type: marker}} ''' assert isinstance(filters, Iterable), 'Invalid filters %s' % filter assert isinstance(replacer, ReplacerWithMarkers), 'Invalid replacer %s' % replacer filtersWithPath = {} for resourceFilter in filters: assert isinstance(resourceFilter, Filter), 'Invalid filter %s' % filter assert isinstance(resourceFilter.filter, IAclFilter), \ 'Invalid filter object %s for resource filter %s' % (resourceFilter.filter, resourceFilter) typeService = typeFor(resourceFilter.filter) assert isinstance(typeService, TypeService), \ 'Invalid filter %s, it needs to be mapped as a REST service' % resourceFilter.filter pathAndMarkers = self._cacheFilters.get(typeService) if not pathAndMarkers: nodes = findNodesFor(self.resourcesRoot, typeService, 'isAllowed') if not nodes: log.error('The filter service %s cannot be located in the resources tree', typeService) continue if len(nodes) > 1: log.error('To many nodes for service %s in the resources tree, don\'t know which one to use', typeService) continue node = nodes[0] assert isinstance(node, Node) path = pathForNode(node) assert isinstance(path, Path) if __debug__: # Just checking that the properties are ok propertyTypes = propertyTypesOf(path, node.get) assert len(propertyTypes) == 2, 'Invalid path %s for filter' % path indexAuth = propertyTypes.index(resourceFilter.authenticated) assert indexAuth >= 0, 'Invalid authenticated %s for path %s' % (resourceFilter.authenticated, path) indexRsc = propertyTypes.index(resourceFilter.resource) assert indexRsc >= 0, 'Invalid resource %s for path %s' % (resourceFilter.resource, path) assert indexAuth < indexRsc, 'Invalid path %s, improper order for types' % path markerAuth = markerFor(resourceFilter.authenticated) replacer.register((markerAuth, '*')) pathMarked = '/'.join(path.toPaths(self.converterPath, replacer)) pathAndMarkers = self._cacheFilters[typeService] = (pathMarked, {resourceFilter.authenticated: markerAuth}) filtersWithPath[resourceFilter.resource] = pathAndMarkers return filtersWithPath
def processPattern(path, invoker, encoder, values=None): ''' Process the gateway pattern creating groups for all property types present in the path. @param path: Path The path to process as a gateway pattern. @param invoker: Invoker The invoker to process the pattern based on. @param encoder: IEncoderPath The path encoder used for processing to path. @param values: dictionary{TypeProperty: string} The static values to be placed on the path, as a key the type property that has the value. @return: tuple(string, list[TypeProperty]) Returns the gateway pattern and the property types that the pattern has capturing groups for. ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(encoder, IEncoderPath), 'Invalid path encoder %s' % encoder replaceMarkers, types = [], [] for propertyType in propertyTypesOf(path, invoker): assert isinstance( propertyType, TypeProperty), 'Invalid property type %s' % propertyType if values: assert isinstance(values, dict), 'Invalid values %s' % values value = values.get(propertyType) if value is not None: assert isinstance(value, str), 'Invalid value %s' % value replaceMarkers.append(re.escape(value)) continue if propertyType.isOf(int): replaceMarkers.append('([0-9\\-]+)') elif propertyType.isOf(str): replaceMarkers.append('([^\\/]+)') else: raise Exception('Unusable type \'%s\'' % propertyType) types.append(propertyType) pattern = encoder.encodePattern( path, invalid=ReplacerWithMarkers().register(replaceMarkers)) return '%s[\\/]?(?:\\.|$)' % pattern, types
def _processPatternWithFilters(self, path, invoker, filtersWithPath, replacer): ''' Process the access pattern for filters by using the path and replacer. @param path: Path The path to process the pattern based on. @param invoker: Invoker The invoker to process the pattern based on. @param filtersWithPath: dictionary{TypeProperty, tuple(string, dictionary{TypeProperty: string}} A dictionary containing {resource type, (marked path, {authenticated type: marker}} @param replacer: PatternReplacer The replacer to use on the path. @return: tuple(string, list[string]|None, dictionary{TypeProperty: string}|None) Basically it returns the (pattern, filters if available, markers if available) ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(replacer, ReplacerWithMarkers), 'Invalid replacer %s' % replacer filters, markers, replaceMarkers = None, None, [] for propertyType in propertyTypesOf(path, invoker): assert isinstance(propertyType, TypeProperty), 'Invalid property type %s' % propertyType pathAndMarkers = filtersWithPath.get(propertyType) if pathAndMarkers: filter, markers = pathAndMarkers if filters is None: filters = [filter] else: filters.append(filter) if markers is None: markers = dict(markers) else: markers.update(markers) isFiltered = True else: isFiltered = False if propertyType.isOf(int): replaceMarkers.append('([0-9\\-]+)' if isFiltered else '[0-9\\-]+') elif propertyType.isOf(str): replaceMarkers.append('([^\\/]+)' if isFiltered else '[^\\/]+') else: raise Exception('Unusable type \'%s\'' % propertyType) replacer.register(replaceMarkers) pattern = ''.join(('\\/'.join(path.toPaths(self.converterPath, replacer)), '[\\/]?(?:\\.|$)')) return pattern, filters, markers
def process(self, node, invoker): ''' Process the structure for the provided node and invoker. @param node: Node The node to process for. @param invoker: Invoker The invoker to process. @return: tuple(list[TypeProperty], @see: StructNode.process)|None The list of path property types. ''' assert isinstance(node, Node), 'Invalid node %s' % node structNode = self.structNodes.get(node) if structNode is None: structNode = self.structNodes[node] = StructNode() assert isinstance(structNode, StructNode) data = structNode.process(invoker) if data is None: return typesPath = self.typesPaths.get(node) if typesPath is None: typesPath = self.typesPaths[node] = (propertyTypesOf(node, invoker),) return typesPath + data
def processPath(path, invoker, encoder, values=None): ''' Process the gateway path marking groups for all property types present in the path. @param path: Path The path to process as a gateway pattern. @param invoker: Invoker The invoker to process the pattern based on. @param encoder: IEncoderPath The path encoder used for processing to path. @param values: dictionary{TypeProperty: string} The static values to be placed on the path, as a key the type property that has the value. @return: tuple(string, list[TypeProperty]) Returns the gateway path and the property types that the path has markers for. ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(encoder, IEncoderPath), 'Invalid path encoder %s' % encoder replaceMarkers, types = [], [] for propertyType in propertyTypesOf(path, invoker): assert isinstance( propertyType, TypeProperty), 'Invalid property type %s' % propertyType if values: assert isinstance(values, dict), 'Invalid values %s' % values value = values.get(propertyType) if value is not None: assert isinstance(value, str), 'Invalid value %s' % value replaceMarkers.append(value) continue replaceMarkers.append('{%s}' % len(types)) types.append(propertyType) return encoder.encode( path, invalid=ReplacerWithMarkers().register(replaceMarkers)), types
def processPattern(path, invoker, encoder, values=None): ''' Process the gateway pattern creating groups for all property types present in the path. @param path: Path The path to process as a gateway pattern. @param invoker: Invoker The invoker to process the pattern based on. @param encoder: IEncoderPath The path encoder used for processing to path. @param values: dictionary{TypeProperty: string} The static values to be placed on the path, as a key the type property that has the value. @return: tuple(string, list[TypeProperty]) Returns the gateway pattern and the property types that the pattern has capturing groups for. ''' assert isinstance(path, Path), 'Invalid path %s' % path assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker assert isinstance(encoder, IEncoderPath), 'Invalid path encoder %s' % encoder replaceMarkers, types = [], [] for propertyType in propertyTypesOf(path, invoker): assert isinstance(propertyType, TypeProperty), 'Invalid property type %s' % propertyType if values: assert isinstance(values, dict), 'Invalid values %s' % values value = values.get(propertyType) if value is not None: assert isinstance(value, str), 'Invalid value %s' % value replaceMarkers.append(re.escape(value)) continue if propertyType.isOf(int): replaceMarkers.append('([0-9\\-]+)') elif propertyType.isOf(str): replaceMarkers.append('([^\\/]+)') else: raise Exception('Unusable type \'%s\'' % propertyType) types.append(propertyType) pattern = encoder.encodePattern(path, invalid=ReplacerWithMarkers().register(replaceMarkers)) return '%s[\\/]?(?:\\.|$)' % pattern, types
def alternates(self): ''' Provides the alternates. @return: dictionary{tuple(Node, Invoker): list[tuple(Node, Invoker, set(TypeProperty))]} The alternates dictionary, as a key a tuple with the node and invoker and as a value a set of the same tuples with the required property types and contains the possible alternates for the key. ''' if self._alternates is None: self._alternates = {} alternatesRepository = {(typeService, call): set(alternates) for typeService, call, alternates in self.alternate.iterate()} # First we process the node and invoker keys keys = [] for node in iterateNodes(self.resourcesRoot): for _method, attr in METHOD_NODE_ATTRIBUTE.items(): invoker = getattr(node, attr) if not invoker: continue keys.append((node, invoker)) # Then we need to find the alternates pathTypesByKey, modelTypesByKey = {}, {} for key in keys: node, invoker = key assert isinstance(node, Node), 'Invalid node %s' % node assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker for keyAlt in keys: nodeAlt, invokerAlt = keyAlt assert isinstance(nodeAlt, Node), 'Invalid node %s' % nodeAlt assert isinstance(invokerAlt, Invoker), 'Invalid invoker %s' % invokerAlt if node == nodeAlt: continue # Same node, no need to process if invoker != invokerAlt: if invoker.method != invokerAlt.method: continue # Not the same method, no need to process if invoker.output != invokerAlt.output: continue # Not the same return, no need to process modelTypes = modelTypesByKey.get(key) if modelTypes is None: modelTypes = [inp.type for inp in invoker.inputs if isinstance(inp.type, TypeModel)] modelTypesByKey[key] = modelTypes modelTypesAlt = modelTypesByKey.get(keyAlt) if modelTypesAlt is None: modelTypesAlt = [inp.type for inp in invokerAlt.inputs if isinstance(inp.type, TypeModel)] modelTypesByKey[keyAlt] = modelTypesAlt if modelTypes != modelTypesAlt: continue # The model types don't match, no need to process pathTypes = pathTypesByKey.get(key) if pathTypes is None: pathTypes = pathTypesByKey[key] = propertyTypesOf(node, invoker) pathTypesAlt = pathTypesByKey.get(keyAlt) if pathTypesAlt is None: pathTypesAlt = pathTypesByKey[keyAlt] = propertyTypesOf(nodeAlt, invokerAlt) required = set(pathTypes) for pathType in pathTypesAlt: try: required.remove(pathType) except KeyError: # If a type is not found it means that they are not compatible required.clear() break if not required: continue # There must be at least one type required # Now we check with the alternates repository configurations if self.processWithRepository(alternatesRepository, invoker, invokerAlt): alternates = self._alternates.get(key) if alternates is None: alternates = self._alternates[key] = [] alternates.append(keyAlt + (required,)) assert log.debug('Added alternate on %s for %s', invoker, invokerAlt) or True for serviceCall, alternates in alternatesRepository.items(): if alternates: alternates = ('\t%s for %s' % serviceCallAlt for serviceCallAlt in alternates) service, call = serviceCall log.error('Invalid alternate configuration on %s for %s with:\n%s\n', service, call, '\n'.join(alternates)) return self._alternates
def alternates(self): ''' Provides the alternates. @return: dictionary{tuple(Node, Invoker): list[tuple(Node, Invoker, set(TypeProperty))]} The alternates dictionary, as a key a tuple with the node and invoker and as a value a set of the same tuples with the required property types and contains the possible alternates for the key. ''' if self._alternates is None: self._alternates = {} alternatesRepository = { (typeService, call): set(alternates) for typeService, call, alternates in self.alternate.iterate() } # First we process the node and invoker keys keys = [] for node in iterateNodes(self.resourcesRoot): for _method, attr in METHOD_NODE_ATTRIBUTE.items(): invoker = getattr(node, attr) if not invoker: continue keys.append((node, invoker)) # Then we need to find the alternates pathTypesByKey, modelTypesByKey = {}, {} for key in keys: node, invoker = key assert isinstance(node, Node), 'Invalid node %s' % node assert isinstance(invoker, Invoker), 'Invalid invoker %s' % invoker for keyAlt in keys: nodeAlt, invokerAlt = keyAlt assert isinstance(nodeAlt, Node), 'Invalid node %s' % nodeAlt assert isinstance( invokerAlt, Invoker), 'Invalid invoker %s' % invokerAlt if node == nodeAlt: continue # Same node, no need to process if invoker != invokerAlt: if invoker.method != invokerAlt.method: continue # Not the same method, no need to process if invoker.output != invokerAlt.output: continue # Not the same return, no need to process modelTypes = modelTypesByKey.get(key) if modelTypes is None: modelTypes = [ inp.type for inp in invoker.inputs if isinstance(inp.type, TypeModel) ] modelTypesByKey[key] = modelTypes modelTypesAlt = modelTypesByKey.get(keyAlt) if modelTypesAlt is None: modelTypesAlt = [ inp.type for inp in invokerAlt.inputs if isinstance(inp.type, TypeModel) ] modelTypesByKey[keyAlt] = modelTypesAlt if modelTypes != modelTypesAlt: continue # The model types don't match, no need to process pathTypes = pathTypesByKey.get(key) if pathTypes is None: pathTypes = pathTypesByKey[key] = propertyTypesOf( node, invoker) pathTypesAlt = pathTypesByKey.get(keyAlt) if pathTypesAlt is None: pathTypesAlt = pathTypesByKey[ keyAlt] = propertyTypesOf(nodeAlt, invokerAlt) required = set(pathTypes) for pathType in pathTypesAlt: try: required.remove(pathType) except KeyError: # If a type is not found it means that they are not compatible required.clear() break if not required: continue # There must be at least one type required # Now we check with the alternates repository configurations if self.processWithRepository(alternatesRepository, invoker, invokerAlt): alternates = self._alternates.get(key) if alternates is None: alternates = self._alternates[key] = [] alternates.append(keyAlt + (required, )) assert log.debug('Added alternate on %s for %s', invoker, invokerAlt) or True for serviceCall, alternates in alternatesRepository.items(): if alternates: alternates = ('\t%s for %s' % serviceCallAlt for serviceCallAlt in alternates) service, call = serviceCall log.error( 'Invalid alternate configuration on %s for %s with:\n%s\n', service, call, '\n'.join(alternates)) return self._alternates
def processFilter(self, rfilter, provider, marker, encoder): ''' Process the provided filter. @param rfilter: Filter The resource filter to process. @param provider: callable|None The callable used in solving the authenticated values. @param marker: string The resource marker to place in the filter path, this marker is used to identify the group in the gateway pattern. @param encoder: IEncoderPath The encoder path to be used for the gateways resource paths and patterns. @return: string|None The marked filter path, None if the filter is invalid. ''' assert isinstance(rfilter, Filter), 'Invalid filter %s' % rfilter assert isinstance( rfilter.filter, IAclFilter), 'Invalid filter %s of %s' % (rfilter.filter, rfilter) typeService = typeFor(rfilter.filter) assert isinstance( typeService, TypeService ), 'Invalid filter %s, is not a REST service' % rfilter.filter assert isinstance(marker, str), 'Invalid marker %s' % marker assert isinstance(encoder, IEncoderPath), 'Invalid encoder path %s' % encoder path = self._cacheFilters.get(typeService) if not path: nodes = findNodesFor(self.resourcesRoot, typeService, 'isAllowed') if not nodes: log.error( 'The filter service %s cannot be located in the resources tree', typeService) return if len(nodes) > 1: log.error( 'To many nodes for service %s in the resources tree, don\'t know which one to use', typeService) return node = nodes[0] assert isinstance(node, Node) path = pathForNode(node) if __debug__: # Just checking that the properties are ok propertyTypes = propertyTypesOf(path, node.get) assert len( propertyTypes) == 2, 'Invalid path %s for filter' % path indexAuth = propertyTypes.index(rfilter.authenticated) assert indexAuth >= 0, 'Invalid authenticated %s for path %s' % ( rfilter.authenticated, path) indexRsc = propertyTypes.index(rfilter.resource) assert indexRsc >= 0, 'Invalid resource %s for path %s' % ( rfilter.resource, path) assert indexAuth < indexRsc, 'Invalid path %s, improper order for types' % path assert isinstance(path, Path), 'Invalid path %s' % path if provider: assert callable( provider), 'Invalid authenticated provider %s' % provider valueAuth = provider(rfilter.authenticated) else: valueAuth = None if valueAuth is None: log.error( 'The filter service %s has not authenticated value for %s', typeService, rfilter.authenticated) return return encoder.encode(path, invalid=ReplacerWithMarkers().register( (valueAuth, marker)), quoted=False)