Exemplo n.º 1
0
    async def _handle_job_request(self,
                                  web_request: WebRequest
                                  ) -> Dict[str, Any]:
        action = web_request.get_action()
        if action == "GET":
            job_id = web_request.get_str("uid")
            if job_id not in self.cached_job_ids:
                raise self.server.error(f"Invalid job uid: {job_id}", 404)
            job = self.history_ns[job_id]
            return {"job": self._prep_requested_job(job, job_id)}
        if action == "DELETE":
            all = web_request.get_boolean("all", False)
            if all:
                deljobs = self.cached_job_ids
                self.history_ns.clear()
                self.cached_job_ids = []
                self.next_job_id = 0
                return {'deleted_jobs': deljobs}

            job_id = web_request.get_str("uid")
            if job_id not in self.cached_job_ids:
                raise self.server.error(f"Invalid job uid: {job_id}", 404)

            self.delete_job(job_id)
            return {'deleted_jobs': [job_id]}
        raise self.server.error("Invalid Request Method")
Exemplo n.º 2
0
 async def _handle_call_agent(self, web_request: WebRequest) -> Any:
     agent = web_request.get_str("agent")
     method: str = web_request.get_str("method")
     args: Optional[Union[List, Dict[str, Any]]]
     args = web_request.get("arguments", None)
     if args is not None and not isinstance(args, (list, dict)):
         raise self.server.error(
             "The 'arguments' field must contain an object or a list")
     if agent not in self.agents:
         raise self.server.error(f"Agent {agent} not connected")
     conn = self.agents[agent]
     return await conn.call_method(method, args)
Exemplo n.º 3
0
 async def _handle_single_power_request(
         self, web_request: WebRequest) -> Dict[str, Any]:
     dev_name: str = web_request.get_str('device')
     req_action = web_request.get_action()
     if dev_name not in self.devices:
         raise self.server.error(f"No valid device named {dev_name}")
     dev = self.devices[dev_name]
     if req_action == 'GET':
         action = "status"
     elif req_action == "POST":
         action = web_request.get_str('action').lower()
         if action not in ["on", "off", "toggle"]:
             raise self.server.error(f"Invalid requested action '{action}'")
     result = await self._process_request(dev, action)
     return {dev_name: result}
Exemplo n.º 4
0
 async def _handle_webcam_request(
         self, web_request: WebRequest) -> Dict[str, Any]:
     action = web_request.get_action()
     name = web_request.get_str("name")
     webcam_data: Dict[str, Any] = {}
     if action == "GET":
         if name not in self.webcams:
             raise self.server.error(f"Webcam {name} not found", 404)
         webcam_data = self.webcams[name].as_dict()
     elif action == "POST":
         if (name in self.webcams
                 and self.webcams[name].source == "config"):
             raise self.server.error(
                 f"Cannot overwrite webcam '{name}' sourced from "
                 "Moonraker configuration")
         webcam = WebCam.from_web_request(self.server, web_request)
         self.webcams[name] = webcam
         webcam_data = webcam.as_dict()
         await self._save_cam(webcam)
     elif action == "DELETE":
         if name not in self.webcams:
             raise self.server.error(f"Webcam {name} not found", 404)
         elif self.webcams[name].source == "config":
             raise self.server.error(
                 f"Cannot delete webcam '{name}' sourced from "
                 "Moonraker configuration")
         webcam = self.webcams.pop(name)
         webcam_data = webcam.as_dict()
         await self._delete_cam(webcam)
     if action != "GET":
         self.server.send_event("webcam:webcams_changed",
                                {"webcams": self._list_webcams()})
     return {"webcam": webcam_data}
Exemplo n.º 5
0
 async def _handle_item_request(self,
                                web_request: WebRequest) -> Dict[str, Any]:
     action = web_request.get_action()
     namespace = web_request.get_str("namespace")
     if namespace in self.forbidden_namespaces:
         raise self.server.error(
             f"Read/Write access to namespace '{namespace}'"
             " is forbidden", 403)
     key: Any
     valid_types: Tuple[type, ...]
     if action != "GET":
         if namespace in self.protected_namespaces:
             raise self.server.error(
                 f"Write access to namespace '{namespace}'"
                 " is forbidden", 403)
         key = web_request.get("key")
         valid_types = (list, str)
     else:
         key = web_request.get("key", None)
         valid_types = (list, str, type(None))
     if not isinstance(key, valid_types):
         raise self.server.error(
             "Value for argument 'key' is an invalid type: "
             f"{type(key).__name__}")
     if action == "GET":
         val = await self.get_item(namespace, key)
     elif action == "POST":
         val = web_request.get("value")
         await self.insert_item(namespace, key, val)
     elif action == "DELETE":
         val = await self.delete_item(namespace, key, drop_empty_db=True)
     return {'namespace': namespace, 'key': key, 'value': val}
Exemplo n.º 6
0
 async def _handle_subscription_request(
         self, web_request: WebRequest) -> Dict[str, Any]:
     topic: str = web_request.get_str("topic")
     qos: int = web_request.get_int("qos", self.qos)
     timeout: Optional[float] = web_request.get_float('timeout', None)
     resp: asyncio.Future = asyncio.Future()
     hdl: Optional[SubscriptionHandle] = None
     try:
         hdl = self.subscribe_topic(topic, resp.set_result, qos)
         self.pending_responses.append(resp)
         await asyncio.wait_for(resp, timeout)
         ret: bytes = resp.result()
     except asyncio.TimeoutError:
         raise self.server.error("MQTT Subscribe Timed Out", 504)
     finally:
         try:
             self.pending_responses.remove(resp)
         except Exception:
             pass
         if hdl is not None:
             self.unsubscribe(hdl)
     try:
         payload = json.loads(ret)
     except json.JSONDecodeError:
         payload = ret.decode()
     return {'topic': topic, 'payload': payload}
Exemplo n.º 7
0
 def _login_jwt_user(self,
                     web_request: WebRequest,
                     create: bool = False) -> Dict[str, Any]:
     username: str = web_request.get_str('username')
     password: str = web_request.get_str('password')
     user_info: Dict[str, Any]
     if username in RESERVED_USERS:
         raise self.server.error(f"Invalid Request for user {username}")
     if create:
         if username in self.users:
             raise self.server.error(f"User {username} already exists")
         salt = secrets.token_bytes(32)
         hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(),
                                           salt, HASH_ITER).hex()
         user_info = {
             'username': username,
             'password': hashed_pass,
             'salt': salt.hex(),
             'created_on': time.time()
         }
         self.users[username] = user_info
         action = "user_created"
     else:
         if username not in self.users:
             raise self.server.error(f"Unregistered User: {username}")
         user_info = self.users[username]
         salt = bytes.fromhex(user_info['salt'])
         hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(),
                                           salt, HASH_ITER).hex()
         action = "user_logged_in"
     if hashed_pass != user_info['password']:
         raise self.server.error("Invalid Password")
     jwt_secret_hex: Optional[str] = user_info.get('jwt_secret', None)
     if jwt_secret_hex is None:
         private_key = Signer()
         jwk_id = base64url_encode(secrets.token_bytes()).decode()
         user_info['jwt_secret'] = private_key.hex_seed().decode()
         user_info['jwk_id'] = jwk_id
         self.users[username] = user_info
         self.public_jwks[jwk_id] = self._generate_public_jwk(private_key)
     else:
         private_key = self._load_private_key(jwt_secret_hex)
         jwk_id = user_info['jwk_id']
     token = self._generate_jwt(username, jwk_id, private_key)
     refresh_token = self._generate_jwt(
         username,
         jwk_id,
         private_key,
         token_type="refresh",
         exp_time=datetime.timedelta(days=self.login_timeout))
     if create:
         IOLoop.current().call_later(.005, self.server.send_event,
                                     "authorization:user_created",
                                     {'username': username})
     return {
         'username': username,
         'token': token,
         'refresh_token': refresh_token,
         'action': action
     }
Exemplo n.º 8
0
 async def _handle_repo_recovery(self,
                                 web_request: WebRequest
                                 ) -> str:
     await self.initialized_lock.wait()
     if await self._check_klippy_printing():
         raise self.server.error(
             "Recovery Attempt Refused: Klippy is printing")
     app: str = web_request.get_str('name')
     hard = web_request.get_boolean("hard", False)
     update_deps = web_request.get_boolean("update_deps", False)
     updater = self.updaters.get(app, None)
     if updater is None:
         raise self.server.error(f"Updater {app} not available", 404)
     elif not isinstance(updater, GitDeploy):
         raise self.server.error(f"Upater {app} is not a Git Repo Type")
     async with self.cmd_request_lock:
         self.cmd_helper.set_update_info(f"recover_{app}", id(web_request))
         try:
             await updater.recover(hard, update_deps)
         except Exception as e:
             self.cmd_helper.notify_update_response(
                 f"Error Recovering {app}")
             self.cmd_helper.notify_update_response(
                 str(e), is_complete=True)
             raise
         finally:
             self.cmd_helper.clear_update_info()
     return "ok"
Exemplo n.º 9
0
    async def _handle_single_wled_request(
            self: WLED, web_request: WebRequest) -> Dict[str, Any]:
        strip_name: str = web_request.get_str('strip')
        preset: int = web_request.get_int('preset', -1)

        req_action = web_request.get_action()
        if strip_name not in self.strips:
            raise self.server.error(f"No valid strip named {strip_name}")
        strip = self.strips[strip_name]
        if req_action == 'GET':
            return {strip_name: strip.get_strip_info()}
        elif req_action == "POST":
            action = web_request.get_str('action').lower()
            if action not in ["on", "off", "toggle"]:
                raise self.server.error(f"Invalid requested action '{action}'")
            result = await self._process_request(strip, action, preset)
        return {strip_name: result}
Exemplo n.º 10
0
    async def _handle_jobs_list(self,
                                web_request: WebRequest
                                ) -> Dict[str, Any]:
        async with self.request_lock:
            i = 0
            count = 0
            end_num = len(self.cached_job_ids)
            jobs: List[Dict[str, Any]] = []
            start_num = 0

            before = web_request.get_float("before", -1)
            since = web_request.get_float("since", -1)
            limit = web_request.get_int("limit", 50)
            start = web_request.get_int("start", 0)
            order = web_request.get_str("order", "desc")

            if order not in ["asc", "desc"]:
                raise self.server.error(f"Invalid `order` value: {order}", 400)

            reverse_order = (order == "desc")

            # cached jobs is asc order, find lower and upper boundary
            if since != -1:
                while start_num < end_num:
                    job_id = self.cached_job_ids[start_num]
                    job: Dict[str, Any] = await self.history_ns[job_id]
                    if job['start_time'] > since:
                        break
                    start_num += 1

            if before != -1:
                while end_num > 0:
                    job_id = self.cached_job_ids[end_num-1]
                    job = await self.history_ns[job_id]
                    if job['end_time'] < before:
                        break
                    end_num -= 1

            if start_num >= end_num or end_num == 0:
                return {"count": 0, "jobs": []}

            i = start
            count = end_num - start_num

            if limit == 0:
                limit = MAX_JOBS

            while i < count and len(jobs) < limit:
                if reverse_order:
                    job_id = self.cached_job_ids[end_num - i - 1]
                else:
                    job_id = self.cached_job_ids[start_num + i]
                job = await self.history_ns[job_id]
                jobs.append(self._prep_requested_job(job, job_id))
                i += 1

            return {"count": count, "jobs": jobs}
Exemplo n.º 11
0
 async def _handle_dismiss_request(
     self, web_request: WebRequest
 ) -> Dict[str, Any]:
     async with self.request_lock:
         entry_id: str = web_request.get_str("entry_id")
         wake_time: Optional[int] = web_request.get_int("wake_time", None)
         await self.entry_mgr.dismiss_entry(entry_id, wake_time)
         return {
             "entry_id": entry_id
         }
Exemplo n.º 12
0
 async def make_request(self, web_request: WebRequest) -> Any:
     rpc_method = web_request.get_endpoint()
     if rpc_method == "objects/subscribe":
         return await self._request_subscripton(web_request)
     else:
         if rpc_method == "gcode/script":
             script = web_request.get_str('script', "")
             data_store: DataStore = self.lookup_component('data_store')
             data_store.store_gcode_command(script)
         return await self._request_standard(web_request)
Exemplo n.º 13
0
 async def _handle_password_reset(
         self, web_request: WebRequest) -> Dict[str, str]:
     password: str = web_request.get_str('password')
     new_pass: str = web_request.get_str('new_password')
     user_info = web_request.get_current_user()
     if user_info is None:
         raise self.server.error("No Current User")
     username = user_info['username']
     if username in RESERVED_USERS:
         raise self.server.error(
             f"Invalid Reset Request for user {username}")
     salt = bytes.fromhex(user_info['salt'])
     hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(), salt,
                                       HASH_ITER).hex()
     if hashed_pass != user_info['password']:
         raise self.server.error("Invalid Password")
     new_hashed_pass = hashlib.pbkdf2_hmac('sha256', new_pass.encode(),
                                           salt, HASH_ITER).hex()
     self.users[f'{username}.password'] = new_hashed_pass
     return {'username': username, 'action': "user_password_reset"}
Exemplo n.º 14
0
 async def _handle_publish_request(
         self, web_request: WebRequest) -> Dict[str, Any]:
     topic: str = web_request.get_str("topic")
     payload: Any = web_request.get("payload", None)
     qos: int = web_request.get_int("qos", self.qos)
     retain: bool = web_request.get_boolean("retain", False)
     timeout: Optional[float] = web_request.get_float('timeout', None)
     try:
         await asyncio.wait_for(
             self.publish_topic(topic, payload, qos, retain), timeout)
     except asyncio.TimeoutError:
         raise self.server.error("MQTT Publish Timed Out", 504)
     return {"topic": topic}
Exemplo n.º 15
0
 async def request(self, web_request: WebRequest) -> Any:
     if not self.is_connected():
         raise ServerError("Klippy Host not connected", 503)
     rpc_method = web_request.get_endpoint()
     if rpc_method == "objects/subscribe":
         return await self._request_subscripton(web_request)
     else:
         if rpc_method == "gcode/script":
             script = web_request.get_str('script', "")
             if script:
                 self.server.send_event(
                     "klippy_connection:gcode_received", script)
         return await self._request_standard(web_request)
Exemplo n.º 16
0
 async def _handle_agent_event(self, web_request: WebRequest) -> str:
     conn = web_request.get_connection()
     if not isinstance(conn, WebSocket):
         raise self.server.error("No connection detected")
     if conn.client_data["type"] != "agent":
         raise self.server.error(
             "Only connections of the 'agent' type can send events")
     name = conn.client_data["name"]
     evt_name = web_request.get_str("event")
     if evt_name in ["connected", "disconnected"]:
         raise self.server.error(f"Event '{evt_name}' is reserved")
     data: Optional[Union[List, Dict[str, Any]]]
     data = web_request.get("data", None)
     evt: Dict[str, Any] = {"agent": name, "event": evt_name}
     if data is not None:
         evt["data"] = data
     conn.send_notification("agent_event", [evt])
     return "ok"
Exemplo n.º 17
0
 async def _handle_refresh_jwt(self,
                               web_request: WebRequest) -> Dict[str, str]:
     refresh_token: str = web_request.get_str('refresh_token')
     try:
         user_info = self._decode_jwt(refresh_token, token_type="refresh")
     except Exception:
         raise self.server.error("Invalid Refresh Token", 401)
     username: str = user_info['username']
     if 'jwt_secret' not in user_info or "jwk_id" not in user_info:
         raise self.server.error("User not logged in", 401)
     private_key = self._load_private_key(user_info['jwt_secret'])
     jwk_id: str = user_info['jwk_id']
     token = self._generate_jwt(username, jwk_id, private_key)
     return {
         'username': username,
         'token': token,
         'action': 'user_jwt_refresh'
     }
Exemplo n.º 18
0
 async def _handle_webcam_test(self,
                               web_request: WebRequest) -> Dict[str, Any]:
     name = web_request.get_str("name")
     if name not in self.webcams:
         raise self.server.error(f"Webcam '{name}' not found", 404)
     client: HttpClient = self.server.lookup_component("http_client")
     cam = self.webcams[name]
     result: Dict[str, Any] = {"name": name, "snapshot_reachable": False}
     for img_type in ["snapshot", "stream"]:
         try:
             func = getattr(cam, f"get_{img_type}_url")
             result[f"{img_type}_url"] = await func(True)
         except Exception:
             logging.exception(f"Error Processing {img_type} url")
             result[f"{img_type}_url"] = ""
     if result.get("snapshot_url", "").startswith("http"):
         url = client.escape_url(result["snapshot_url"])
         ret = await client.get(url, connect_timeout=1., request_timeout=1.)
         result["snapshot_reachable"] = not ret.has_error()
     return result
Exemplo n.º 19
0
 def _delete_jwt_user(self, web_request: WebRequest) -> Dict[str, str]:
     username: str = web_request.get_str('username')
     current_user = web_request.get_current_user()
     if current_user is not None:
         curname = current_user.get('username', None)
         if curname is not None and curname == username:
             raise self.server.error(
                 f"Cannot delete logged in user {curname}")
     if username in RESERVED_USERS:
         raise self.server.error(
             f"Invalid Request for reserved user {username}")
     user_info: Optional[Dict[str, Any]] = self.users.get(username)
     if user_info is None:
         raise self.server.error(f"No registered user: {username}")
     self.public_jwks.pop(self.users.get(f"{username}.jwk_id"), None)
     del self.users[username]
     IOLoop.current().call_later(.005, self.server.send_event,
                                 "authorization:user_deleted",
                                 {'username': username})
     return {"username": username, "action": "user_deleted"}
Exemplo n.º 20
0
 def from_web_request(cls, server: Server,
                      web_request: WebRequest) -> WebCam:
     webcam: Dict[str, Any] = {}
     webcam["name"] = web_request.get_str("name")
     webcam["location"] = web_request.get_str("location", "printer")
     webcam["service"] = web_request.get_str("service", "mjpegstreamer")
     webcam["target_fps"] = web_request.get_int("target_fps", 15)
     webcam["stream_url"] = web_request.get_str("stream_url")
     webcam["snapshot_url"] = web_request.get_str("snapshot_url")
     webcam["flip_horizontal"] = web_request.get_boolean(
         "flip_horizontal", False)
     webcam["flip_vertical"] = web_request.get_boolean(
         "flip_vertical", False)
     webcam["rotation"] = web_request.get_str("rotation", 0)
     if webcam["rotation"] not in [0, 90, 180, 270]:
         raise server.error("Invalid value for parameter 'rotate'")
     webcam["source"] = "database"
     return cls(server, **webcam)
Exemplo n.º 21
0
 async def _gcode_start_print(self, web_request: WebRequest) -> str:
     filename: str = web_request.get_str('filename')
     return await self.start_print(filename)
Exemplo n.º 22
0
 async def _login_jwt_user(self,
                           web_request: WebRequest,
                           create: bool = False) -> Dict[str, Any]:
     username: str = web_request.get_str('username')
     password: str = web_request.get_str('password')
     source: str = web_request.get_str('source',
                                       self.default_source).lower()
     if source not in AUTH_SOURCES:
         raise self.server.error(f"Invalid 'source': {source}")
     user_info: Dict[str, Any]
     if username in RESERVED_USERS:
         raise self.server.error(f"Invalid Request for user {username}")
     if source == "ldap":
         if create:
             raise self.server.error("Cannot Create LDAP User")
         if self.ldap is None:
             raise self.server.error("LDAP authentication not available",
                                     401)
         await self.ldap.authenticate_ldap_user(username, password)
         if username not in self.users:
             create = True
     if create:
         if username in self.users:
             raise self.server.error(f"User {username} already exists")
         salt = secrets.token_bytes(32)
         hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(),
                                           salt, HASH_ITER).hex()
         user_info = {
             'username': username,
             'password': hashed_pass,
             'salt': salt.hex(),
             'source': source,
             'created_on': time.time()
         }
         self.users[username] = user_info
         self._sync_user(username)
         action = "user_created"
         if source == "ldap":
             # Dont notify user created
             action = "user_logged_in"
             create = False
     else:
         if username not in self.users:
             raise self.server.error(f"Unregistered User: {username}")
         user_info = self.users[username]
         auth_src = user_info.get("source", "moonraker")
         if auth_src != source:
             raise self.server.error(
                 f"Moonraker cannot authenticate user '{username}', must "
                 f"specify source '{auth_src}'", 401)
         salt = bytes.fromhex(user_info['salt'])
         hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(),
                                           salt, HASH_ITER).hex()
         action = "user_logged_in"
         if hashed_pass != user_info['password']:
             raise self.server.error("Invalid Password")
     jwt_secret_hex: Optional[str] = user_info.get('jwt_secret', None)
     if jwt_secret_hex is None:
         private_key = Signer()
         jwk_id = base64url_encode(secrets.token_bytes()).decode()
         user_info['jwt_secret'] = private_key.hex_seed().decode()
         user_info['jwk_id'] = jwk_id
         self.users[username] = user_info
         self._sync_user(username)
         self.public_jwks[jwk_id] = self._generate_public_jwk(private_key)
     else:
         private_key = self._load_private_key(jwt_secret_hex)
         jwk_id = user_info['jwk_id']
     token = self._generate_jwt(username, jwk_id, private_key)
     refresh_token = self._generate_jwt(
         username,
         jwk_id,
         private_key,
         token_type="refresh",
         exp_time=datetime.timedelta(days=self.login_timeout))
     if create:
         event_loop = self.server.get_event_loop()
         event_loop.delay_callback(.005, self.server.send_event,
                                   "authorization:user_created",
                                   {'username': username})
     return {
         'username': username,
         'token': token,
         'source': user_info.get("source", "moonraker"),
         'refresh_token': refresh_token,
         'action': action
     }