def unregisterPublisher(self, caller_id, topic, caller_api, client_ip_address): """ Unregister the caller as a publisher of the topic. @param caller_id: ROS caller id @type caller_id: str @param topic: Fully-qualified name of topic to unregister. @type topic: str @param caller_api str: API URI of service to unregister. Unregistration will only occur if current registration matches. @type caller_api: str @return: (code, statusMessage, numUnregistered). If numUnregistered is zero it means that the caller was not registered as a publisher. The call still succeeds as the intended final state is reached. @rtype: (int, str, int) """ if not is_uri_match(caller_api, client_ip_address): self.logger.warn( "unregisterPublisher( %s, %s, %s, %s ) caller_api not authorized" % (caller_id, topic, caller_api, client_ip_address)) return -1, "caller_api not authorized", [] try: self.ps_lock.acquire() retval = self.reg_manager.unregister_publisher( topic, caller_id, caller_api) if retval[VAL]: self._notify_topic_subscribers( topic, self.publishers.get_apis(topic), self.subscribers.get_apis(topic)) mloginfo("-PUB [%s] %s %s", topic, caller_id, caller_api) finally: self.ps_lock.release() return retval
def unsubscribeParam(self, caller_id, caller_api, key, client_ip_address): """ Retrieve parameter value from server and subscribe to updates to that param. See paramUpdate() in the Node API. @param caller_id str: ROS caller id @type caller_id: str @param key: parameter to lookup. @type key: str @param caller_api: API URI for paramUpdate callbacks. @type caller_api: str @return: [code, statusMessage, numUnsubscribed]. If numUnsubscribed is zero it means that the caller was not subscribed to the parameter. @rtype: [int, str, int] """ if not is_uri_match(caller_api, client_ip_address): self.logger.warn( "unsubscribeParam( %s, %s, %s, %s) caller_api not authorized" % (caller_id, caller_api, key, client_ip_address)) return -1, "caller_api not authorized", 0 key = resolve_name(key, caller_id) try: # ps_lock is required due to potential self.reg_manager modification self.ps_lock.acquire() retval = self.param_server.unsubscribe_param( key, (caller_id, caller_api)) finally: self.ps_lock.release() return 1, "Unsubscribe to parameter [%s]" % key, 1
def unregisterService(self, caller_id, service, service_api, client_ip_address): """ Unregister the caller as a provider of the specified service. @param caller_id str: ROS caller id @type caller_id: str @param service: Fully-qualified name of service @type service: str @param service_api: API URI of service to unregister. Unregistration will only occur if current registration matches. @type service_api: str @return: (code, message, numUnregistered). Number of unregistrations (either 0 or 1). If this is zero it means that the caller was not registered as a service provider. The call still succeeds as the intended final state is reached. @rtype: (int, str, int) """ if not is_uri_match(service_api, client_ip_address): self.logger.warn( "unregisterService( %s, %s, %s, %s ) service_api not authorized" % (caller_id, service, service_api, client_ip_address)) return -1, "service_api not authorized", 0 try: self.ps_lock.acquire() retval = self.reg_manager.unregister_service( service, caller_id, service_api) mloginfo("-SERVICE [%s] %s %s", service, caller_id, service_api) return retval finally: self.ps_lock.release()
def publisherUpdate(self, caller_id, topic, publishers, client_ip_address): """ Callback from master of current publisher list for specified topic. @param caller_id: ROS caller id @type caller_id: str @param topic str: topic name @type topic: str @param publishers: list of current publishers for topic in the form of XMLRPC URIs @type publishers: [str] @return: [code, status, ignore] @rtype: [int, str, int] """ """ Check if request is from master. if so, no need to check if publisher URI is authorized """ if not is_uri_match(self.masterUri, client_ip_address): auth_logger.warn( "publisherUpdate( %s, %s, %s ) method not authorized" % (caller_id, topic, client_ip_address)) return -1, "method not authorized", [] auth_logger.info("publisherUpdate( %s, %s, %s ): OK" % (caller_id, topic, client_ip_address)) auth_logger.debug(" with %s" % publishers) if self.reg_man: for uri in publishers: self.reg_man.publisher_update(topic, publishers) return 1, "", 0
def subscribeParam(self, caller_id, caller_api, key, client_ip_address): """ Retrieve parameter value from server and subscribe to updates to that param. See paramUpdate() in the Node API. @param caller_id str: ROS caller id @type caller_id: str @param key: parameter to lookup. @type key: str @param caller_api: API URI for paramUpdate callbacks. @type caller_api: str @return: [code, statusMessage, parameterValue]. If code is not 1, parameterValue should be ignored. parameterValue is an empty dictionary if the parameter has not been set yet. @rtype: [int, str, XMLRPCLegalValue] """ key = resolve_name(key, caller_id) if not is_uri_match(caller_api, client_ip_address): self.logger.warn( "subscribeParam( %s, %s, %s, %s) caller_api not authorized" % (caller_id, caller_api, key, client_ip_address)) return -1, "caller_api not authorized", 0 if not self.auth.check_param_setter(key, client_ip_address): self.logger.warn( "subscribeParam( %s, %s, %s, %s) parameter not authorized" % (caller_id, caller_api, key, client_ip_address)) return -1, "parameter not authorized", 0 try: # ps_lock has precedence and is required due to # potential self.reg_manager modification self.ps_lock.acquire() val = self.param_server.subscribe_param(key, (caller_id, caller_api)) finally: self.ps_lock.release() return 1, "Subscribed to parameter [%s]" % key, val
def getPid(self, caller_id, client_ip_address): """ Get the PID of this server @param caller_id: ROS caller id @type caller_id: str @return: [1, "", serverProcessPID] @rtype: [int, str, int] """ if is_uri_match(self.masterUri, client_ip_address): auth_logger.warn("shutdown( %s, '%s', %s ) method not authorized" % (caller_id, msg, client_ip_address)) return 1, "", os.getpid()
def getBusInfo(self, caller_id, client_ip_address): """ Retrieve transport/topic connection information @param caller_id: ROS caller id @type caller_id: str """ """ Only master is authorized to call this method """ if is_uri_match(self.masterUri, client_ip_address): auth_logger.warn("getBusInfo( %s, %s ) method not authorized" % (caller_id, client_ip_address)) return 1, "bus info", get_topic_manager().get_pub_sub_info() else: return -1, "method not authorized", []
def getPublications(self, caller_id, client_ip_address): """ Retrieve a list of topics that this node publishes. @param caller_id: ROS caller id @type caller_id: str @return: list of topics published by this node. @rtype: [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]] """ if not is_uri_match(self.masterUri, client_ip_address): auth_logger.warn( "getPublications( %s, %s ) method not authorized" % (caller_id, client_ip_address)) return -1, "method not authorized", [] return 1, "publications", get_topic_manager().get_publications()
def lookupNode(self, caller_id, node_name, client_ip_address): """ Get the XML-RPC URI of the node with the associated name/caller_id. A caller_id can get this information only if it has a relation with this node, ie, it is a subscriber or publisher to a topic with this node. This API is for looking information about publishers and subscribers. Use lookupService instead to lookup ROS-RPC URIs. SecureROS: This will be called by publisher (client_ip_address) to subscriber (node_name) @param caller_id: ROS caller id @type caller_id: str @param node: name of node to lookup @type node: str @return: (code, msg, URI) @rtype: (int, str, str) """ try: self.ps_lock.acquire() node = self.reg_manager.get_node(node_name) if node is not None: retval = 1, "node api", node.api else: retval = -1, "unknown node [%s]" % node_name, '' finally: self.ps_lock.release() if is_uri_match(self.uri, client_ip_address): """ all requests from master are authorized """ return retval else: """ Allow if node is the subscriber to a topic for which client_ip_address is a peer_publisher """ if node is not None: if self.auth.check_peer_publisher_to_api( node.api, client_ip_address): self.auth.logger.info( "lookupNode( %s, %s, %s ): OK (%s is peer publisher to %s)" % (caller_id, node_name, client_ip_address, client_ip_address, node.api)) return retval else: self.auth.logger.info( "lookupNode( %s, %s, %s ): No (%s is not peer publisher to %s)" % (caller_id, node_name, client_ip_address, client_ip_address, node.api)) return -1, "node_name not authorized", '' self.auth.logger.warn( "lookupNode( %s, %s, %s ): node_name is not valid" % (caller_id, node_name, client_ip_address)) return -1, "node_name not authorized", ''
def shutdown(self, caller_id, msg, client_ip_address): """ Stop this server @param caller_id: ROS caller id @type caller_id: str @param msg: a message describing why the node is being shutdown. (XXX no longer an optional argument) @type msg: str @return: [code, msg, 0] @rtype: [int, str, int] """ if not is_uri_match(self.masterUri, client_ip_address): auth_logger.warn("shutdown( %s, '%s', %s ) method not authorized" % (caller_id, msg, client_ip_address)) return -1, "method not authorized", 0 if self._shutdown('external shutdown request from [%s]: %s' % (caller_id, msg)): signal_shutdown('external shutdown request from [%s]: [%s]' % (caller_id, msg)) return 1, "shutdown", 0
def getBusStats(self, caller_id, client_ip_address): """ Retrieve transport/topic statistics @param caller_id: ROS caller id @type caller_id: str @return: [publishStats, subscribeStats, serviceStats]:: publishStats: [[topicName, messageDataSent, pubConnectionData]...[topicNameN, messageDataSentN, pubConnectionDataN]] pubConnectionData: [connectionId, bytesSent, numSent, connected]* . subscribeStats: [[topicName, subConnectionData]...[topicNameN, subConnectionDataN]] subConnectionData: [connectionId, bytesReceived, dropEstimate, connected]* . dropEstimate is -1 if no estimate. serviceStats: not sure yet, probably akin to [numRequests, bytesReceived, bytesSent] """ is_master = is_uri_match(self.masterUri, client_ip_address) pub_stats, sub_stats = get_topic_manager().get_pub_sub_stats() #TODO: serviceStats if is_master: return 1, '', [pub_stats, sub_stats, []] else: return -1, "Only master authorized to call getBusStats()", []
def is_subscriber_authorized(topic, client_ip_address): """ Get list of subscriber nodes from master for each topic. For this given topic, check if the caller node is a valid node. If the caller node is valid, get the URI for this node and check if this URI matches the caller IP address (client_ip_address). We use the valid URI list from master for this topic and checking if the client_ip_address of the caller matches with one of the valid URIs. @param topic: Topic for which subscriber authorization is requested @param client_ip_address: Client IP address for which authorization check is requested @return: If client_ip_address is authorized for topic @rtype: bool """ masterUri = rosgraph.get_master_uri() this_caller_id = rospy.names.get_caller_id() auth_logger.debug("Getting subscribers for topic [%s]" % topic) subs = get_subscriber_list(masterUri, topic, this_caller_id) auth_logger.debug("- [%s]" % (','.join('%s' % n for n in subs))) if len(subs) == 0: return False """ We want to check if client_ip_address matches one of n (for each n in subs) This node is the publisher and n is the subscriber """ for n in subs: code, msg, uri = xmlrpcapi(masterUri).lookupNode(this_caller_id, n) auth_logger.debug( "lookupNode( %s, %s ) returned (code=%s, msg=%s, uri=%s)" % (this_caller_id, n, code, msg, uri)) if code > 0: if is_uri_match(uri, client_ip_address): auth_logger.info( "Subscriber %s XMLRPC URI (%s) matches client IP (%s)" % (n, uri, client_ip_address)) return True else: auth_logger.debug( "Subscriber %s XMLRPC URI (%s) does not match client IP (%s)" % (n, uri, client_ip_address)) auth_logger.warn("Client (%s) not authorized for topic %s" % (client_ip_address, topic)) return False
def shutdown(self, caller_id, msg, client_ip_address): """ Stop this server @param caller_id: ROS caller id @type caller_id: str @param msg: a message describing why the node is being shutdown. (XXX no longer an optional argument) @type msg: str @return: [code, msg, 0] @rtype: [int, str, int] """ if not is_uri_match(self.uri, client_ip_address): self.logger.warn("shutdown( %s, %s ) method not authorized" % (caller_id, client_ip_address)) return -1, "shutdown not authorized", 0 if msg: print("shutdown request: %s" % msg, file=sys.stdout) else: print("shutdown request", file=sys.stdout) self._shutdown('external shutdown request from [%s]: %s' % (caller_id, msg)) return 1, "shutdown", 0
def getSystemState(self, caller_id, client_ip_address): """ Retrieve list representation of system state (i.e. publishers, subscribers, and services). @param caller_id: ROS caller id @type caller_id: str @rtype: (int, str, [[str,[str]], [str,[str]], [str,[str]]]) @return: (code, statusMessage, systemState). System state is in list representation:: [publishers, subscribers, services]. publishers is of the form:: [ [topic1, [topic1Publisher1...topic1PublisherN]] ... ] subscribers is of the form:: [ [topic1, [topic1Subscriber1...topic1SubscriberN]] ... ] services is of the form:: [ [service1, [service1Provider1...service1ProviderN]] ... ] """ edges = [] try: self.ps_lock.acquire() retval = [ r.get_state() for r in (self.publishers, self.subscribers, self.services) ] finally: self.ps_lock.release() if not is_uri_match(self.uri, client_ip_address): pubs = [[t, tpubs] for [t, tpubs] in retval[0] if self.auth.check_subscriber(t, client_ip_address)] subs = [[t, tsubs] for [t, tsubs] in retval[1] if self.auth.check_publisher(t, client_ip_address)] services = retval[2] retval = [pubs, subs, services] return 1, "current system state", retval
def paramUpdate(self, caller_id, parameter_key, parameter_value, client_ip_address): """ Callback from master of current publisher list for specified topic. @param caller_id: ROS caller id @type caller_id: str @param parameter_key str: parameter name, globally resolved @type parameter_key: str @param parameter_value New parameter value @type parameter_value: XMLRPC-legal value @return: [code, status, ignore]. If code is -1 ERROR, the node is not subscribed to parameter_key @rtype: [int, str, int] """ if not is_uri_match(self.masterUri, client_ip_address): auth_logger.warn( "paramUpdate( %s, %s, %s ) method not authorized" % (caller_id, parameter_key, client_ip_address)) return -1, "method not authorized", [] try: get_param_server_cache().update(parameter_key, parameter_value) return 1, '', 0 except KeyError: return -1, 'not subscribed', 0