def _check_auth(self, req: Request): if self.auth_manager is None: return if req.get_auth() is None: self._respond_with_error(req, "NeAuthError", "The bridge only accepts requests based on privileges") raise AuthError() auth = req.get_auth() try: if isinstance(auth, CredentialsAuthContainer): self.auth_manager.authenticate(auth.username, auth.password) self.auth_manager.check_path_access_level_for_user(auth.username, req.get_path()) elif isinstance(auth, SerialAuthContainer): self.auth_manager.check_path_access_level(ApiAccessLevel.admin, req.get_path()) elif isinstance(auth, MqttAuthContainer): self.auth_manager.check_path_access_level(ApiAccessLevel.mqtt, req.get_path()) else: self._respond_with_error(req, "UnknownAuthError", "Unknown error occurred") raise AuthError() except AuthenticationFailedException: self._respond_with_error(req, "WrongAuthError", "illegal combination of username and password") raise AuthError() except UserDoesNotExistException: self._respond_with_error(req, "UserDoesntExistError", "User does not exist") raise AuthError() except InsufficientAccessPrivilegeException: self._respond_with_error(req, "AccessLevelError", "Insufficient privileges") raise AuthError() except UnknownUriException: self._handle_unknown(req) raise AuthError()
def _handle_request(self, req: Request): self._log_request(req) try: self._check_auth(req) except AuthError: return switcher = { ApiURIs.heartbeat.uri: self._handle_heartbeat, ApiURIs.sync_client.uri: self._handle_client_sync, ApiURIs.info_bridge.uri: self._handle_info_bridge, ApiURIs.info_gadgets.uri: self._handle_info_gadgets, ApiURIs.info_clients.uri: self._handle_info_clients, ApiURIs.update_gadget.uri: self._handle_update_gadget, ApiURIs.client_reboot.uri: self._handle_client_reboot, ApiURIs.client_config_write.uri: self._handle_client_config_write, ApiURIs.client_config_delete.uri: self._handle_client_config_delete, ApiURIs.config_storage_get_all.uri: self._handle_get_all_configs, ApiURIs.config_storage_get.uri: self._handle_get_config, ApiURIs.config_storage_save.uri: self._handle_save_config, ApiURIs.config_storage_delete.uri: self._handle_delete_config, ApiURIs.bridge_update_check.uri: self._handle_check_bridge_for_update, ApiURIs.bridge_update_execute.uri: self._handle_bridge_update } handler: Callable[[Request], None] = switcher.get(req.get_path(), self._handle_unknown) handler(req)
class RestServerRequestManager: _request: Request _response: Optional[Request] def __init__(self, hostname: str, path: str, payload: dict, auth: Optional[AuthContainer]): self._response = None self._create_incoming_request(hostname, path, payload, auth) def __del__(self): pass def _create_incoming_request(self, hostname: str, path: str, payload: dict, auth: Optional[AuthContainer]): session_id = random.randint(0, 30000) sender = f"rest_client_{session_id}" self._request = Request(path=path, session_id=session_id, sender=sender, receiver=hostname, payload=payload) if auth is not None: self._request.set_auth(auth) self._request.set_callback_method(self._get_response_function()) def _get_response_function(self) -> response_callback_type: def respond(req: Request, payload: dict, path: Optional[str]): if req.get_session_id() != self._request.get_session_id(): raise IllegalResponseException( f"Session IDs {req.get_session_id()} and " f"{self._request.get_session_id()} are not matching") if path is not None: res_path = path else: res_path = req.get_path() self._response = Request(path=res_path, session_id=req.get_session_id(), sender=self._request.get_receiver(), receiver=req.get_sender(), payload=payload) return respond def get_request(self) -> Request: return self._request def await_response(self, timeout: int = 2) -> Request: start_time = datetime.now() while self._response is None: time.sleep(0.1) now = datetime.now() if now > (start_time + timedelta(seconds=timeout)): break if self._response is None: raise NoResponseReceivedError(self._request.get_path()) return self._response
def mock_response_function(req: Request, payload: dict, path: str): if path is None: buf_path = req.get_path() else: buf_path = path out_req = Request(path=buf_path, session_id=req.get_session_id(), sender=req.get_receiver(), receiver=req.get_sender(), payload=payload) self._last_response = out_req
def _send(self, req: Request): """Sends a request on the serial port""" json_str = json.dumps(req.get_body()) req_line = "!r_p[{}]_b[{}]_\n".format(req.get_path(), json_str) self._logger.debug("Sending: {}".format(req_line[:-1])) out_data = req_line.encode() bytes_written = self._serial_client.write(out_data) if not bytes_written == len(out_data): self._logger.error( f"Problem sending request: only {bytes_written} of {len(out_data)} bytes written." )
def _respond_to(self, req: Request, payload: dict, path: Optional[str] = None): if path: out_path = path else: out_path = req.get_path() receiver = req.get_sender() out_req = Request(out_path, req.get_session_id(), self._host_name, receiver, payload) self._send(out_req)
def respond(req: Request, payload: dict, path: Optional[str]): if req.get_session_id() != self._request.get_session_id(): raise IllegalResponseException( f"Session IDs {req.get_session_id()} and " f"{self._request.get_session_id()} are not matching") if path is not None: res_path = path else: res_path = req.get_path() self._response = Request(path=res_path, session_id=req.get_session_id(), sender=self._request.get_receiver(), receiver=req.get_sender(), payload=payload)
def _format_request(req: Request) -> str: req_obj = {"path": req.get_path(), "body": req.get_body()} req_str = json.dumps(req_obj) return req_str
def _format_request(req: Request) -> str: json_str = json.dumps(req.get_body()) req_line = "!r_p[{}]_b[{}]_\n".format(req.get_path(), json_str) return req_line
def send_request_split(self, path: str, receiver: str, payload: dict, part_max_size: int = 30, timeout: Optional[int] = None) -> Optional[Request]: """ Sends a request to all attached networks. The request will be split into individual parts with a maximum length. :param path: Path to send the request on :param receiver: Receiver for the Request :param payload: Payload to be send :param part_max_size: Maximum size in bytes for each individual payload chunk :param timeout: Maximum timeout to wait for an answer :return: The response if there is any """ if timeout is None: timeout = self._default_timeout req = Request(path, None, self._hostname, receiver, payload) session_id = req.get_session_id() path = req.get_path() sender = req.get_sender() receiver = req.get_receiver() payload_str = json.dumps(req.get_payload()) # Make string ready to be contained in json itself payload_str = payload_str.replace('"', "$*$") payload_len = len(payload_str) parts = [] start = 0 package_index = 0 while start < payload_len: end = start + part_max_size payload_part = payload_str[start:( end if end < payload_len else payload_len)] parts.append(payload_part) start = end last_index = len(parts) for payload_part in parts: out_dict = { "package_index": package_index, "split_payload": payload_part } if package_index == 0: out_dict["last_index"] = last_index out_req = Request(path, session_id, sender, receiver, out_dict) if package_index == last_index - 1: responses = self._send_request_obj(out_req, timeout, 1) if not responses: return None return responses[0] else: self._send_request_obj(out_req, 0, 0) package_index += 1 sleep(0.1) return None