From 54e280bd6a825cb80408a1f3a8f8eafc15069d5c Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 16 Dec 2025 09:49:59 +0100 Subject: [PATCH] prevent access to some more commands by non admins --- music_assistant/controllers/config.py | 22 +++++++++---------- .../controllers/players/player_controller.py | 6 ++--- .../controllers/webserver/controller.py | 16 -------------- .../webserver/remote_access/__init__.py | 4 +++- 4 files changed, 17 insertions(+), 31 deletions(-) diff --git a/music_assistant/controllers/config.py b/music_assistant/controllers/config.py index 8781fa19..9fe45d1e 100644 --- a/music_assistant/controllers/config.py +++ b/music_assistant/controllers/config.py @@ -285,7 +285,7 @@ class ConfigController: return_type: None = ..., ) -> ConfigValueType: ... - @api_command("config/providers/get_value") + @api_command("config/providers/get_value", required_role="admin") async def get_provider_config_value( self, instance_id: str, @@ -323,7 +323,7 @@ class ConfigController: self._value_cache[cache_key] = val return val - @api_command("config/providers/get_entries") + @api_command("config/providers/get_entries", required_role="admin") async def get_provider_config_entries( # noqa: PLR0915 self, provider_domain: str, @@ -487,7 +487,7 @@ class ConfigController: return self.remove(conf_key) - @api_command("config/players") + @api_command("config/players", required_role="admin") async def get_player_configs( self, provider: str | None = None, include_values: bool = False ) -> list[PlayerConfig]: @@ -506,7 +506,7 @@ class ConfigController: and (provider in (None, raw_conf["provider"])) ] - @api_command("config/players/get") + @api_command("config/players/get", required_role="admin") async def get_player_config( self, player_id: str, @@ -533,7 +533,7 @@ class ConfigController: msg = f"No config found for player id {player_id}" raise KeyError(msg) - @api_command("config/players/get_entries") + @api_command("config/players/get_entries", required_role="admin") async def get_player_config_entries( self, player_id: str, @@ -600,7 +600,7 @@ class ConfigController: return_type: None = ..., ) -> ConfigValueType: ... - @api_command("config/players/get_value") + @api_command("config/players/get_value", required_role="admin") async def get_player_config_value( self, player_id: str, @@ -732,7 +732,7 @@ class ConfigController: conf_key = f"{CONF_PLAYERS}/{player_id}/default_name" self.set(conf_key, default_name) - @api_command("config/players/dsp/get") + @api_command("config/players/dsp/get", required_role="admin") def get_player_dsp_config(self, player_id: str) -> DSPConfig: """ Return the DSP Configuration for a player. @@ -812,7 +812,7 @@ class ConfigController: ) return config - @api_command("config/dsp_presets/get") + @api_command("config/dsp_presets/get", required_role="admin") async def get_dsp_presets(self) -> list[DSPConfigPreset]: """Return all user-defined DSP presets.""" raw_presets = self.get(CONF_PLAYER_DSP_PRESETS, {}) @@ -950,7 +950,7 @@ class ConfigController: for core_controller in CONFIGURABLE_CORE_CONTROLLERS ] - @api_command("config/core/get") + @api_command("config/core/get", required_role="admin") async def get_core_config(self, domain: str) -> CoreConfig: """Return configuration for a single core controller.""" raw_conf = self.get(f"{CONF_CORE}/{domain}", {"domain": domain}) @@ -987,7 +987,7 @@ class ConfigController: return_type: None = ..., ) -> ConfigValueType: ... - @api_command("config/core/get_value") + @api_command("config/core/get_value", required_role="admin") async def get_core_config_value( self, domain: str, @@ -1019,7 +1019,7 @@ class ConfigController: else conf.values[key].default_value ) - @api_command("config/core/get_entries") + @api_command("config/core/get_entries", required_role="admin") async def get_core_config_entries( self, domain: str, diff --git a/music_assistant/controllers/players/player_controller.py b/music_assistant/controllers/players/player_controller.py index 3a3704b0..61164915 100644 --- a/music_assistant/controllers/players/player_controller.py +++ b/music_assistant/controllers/players/player_controller.py @@ -1206,7 +1206,7 @@ class PlayerController(CoreController): for player_id in list(player_ids): await self.cmd_ungroup(player_id) - @api_command("players/create_group_player") + @api_command("players/create_group_player", required_role="admin") async def create_group_player( self, provider: str, name: str, members: list[str], dynamic: bool = True ) -> Player: @@ -1233,7 +1233,7 @@ class PlayerController(CoreController): f"Provider {provider} does not support creating group players" ) - @api_command("players/remove_group_player") + @api_command("players/remove_group_player", required_role="admin") async def remove_group_player(self, player_id: str) -> None: """ Remove a group player. @@ -1418,7 +1418,7 @@ class PlayerController(CoreController): self.delete_player_config(player_id) self.mass.signal_event(EventType.PLAYER_REMOVED, player_id) - @api_command("players/remove") + @api_command("players/remove", required_role="admin") async def remove(self, player_id: str) -> None: """ Remove a player from a provider. diff --git a/music_assistant/controllers/webserver/controller.py b/music_assistant/controllers/webserver/controller.py index 01183fcb..9cb62263 100644 --- a/music_assistant/controllers/webserver/controller.py +++ b/music_assistant/controllers/webserver/controller.py @@ -218,9 +218,6 @@ class WebserverController(CoreController): # add info routes.append(("GET", "/info", self._handle_server_info)) routes.append(("OPTIONS", "/info", self._handle_cors_preflight)) - # add logging - routes.append(("GET", "/music-assistant.log", self._handle_application_log)) - routes.append(("OPTIONS", "/music-assistant.log", self._handle_cors_preflight)) # add websocket api routes.append(("GET", "/ws", self._handle_ws_client)) # also host the image proxy on the webserver @@ -539,19 +536,6 @@ class WebserverController(CoreController): self.logger.exception("Error executing command %s: %s", command_msg.command, error) return web.Response(status=500, text="Internal server error") - async def _handle_application_log(self, request: web.Request) -> web.Response: - """Handle request to get the application log.""" - log_data = await self.mass.get_application_log() - return web.Response( - text=log_data, - content_type="text/text", - headers={ - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - }, - ) - async def _handle_api_intro(self, request: web.Request) -> web.Response: """Handle request for API introduction/documentation page.""" intro_html_path = str(RESOURCES_DIR.joinpath("api_docs.html")) diff --git a/music_assistant/controllers/webserver/remote_access/__init__.py b/music_assistant/controllers/webserver/remote_access/__init__.py index 0096590d..c4520aea 100644 --- a/music_assistant/controllers/webserver/remote_access/__init__.py +++ b/music_assistant/controllers/webserver/remote_access/__init__.py @@ -272,7 +272,9 @@ class RemoteAccessManager: return await get_remote_access_info() self._on_unload_callbacks.append( - self.mass.register_api_command("remote_access/info", get_remote_access_info) + self.mass.register_api_command( + "remote_access/info", get_remote_access_info, required_role="admin" + ) ) self._on_unload_callbacks.append( self.mass.register_api_command( -- 2.34.1