Refactor radio endpoint requests to remove API key requirement and implement user role checks for permissions
This commit is contained in:
@@ -23,6 +23,7 @@ from fastapi import (
|
||||
Depends)
|
||||
from fastapi_throttle import RateLimiter
|
||||
from fastapi.responses import RedirectResponse, JSONResponse, FileResponse
|
||||
from auth.deps import get_current_user
|
||||
|
||||
class Radio(FastAPI):
|
||||
"""Radio Endpoints"""
|
||||
@@ -52,9 +53,9 @@ class Radio(FastAPI):
|
||||
if endpoint == "radio/album_art":
|
||||
methods = ["GET"]
|
||||
app.add_api_route(
|
||||
f"/{endpoint}", handler, methods=methods, include_in_schema=False,
|
||||
f"/{endpoint}", handler, methods=methods, include_in_schema=True,
|
||||
dependencies=[Depends(
|
||||
RateLimiter(times=10, seconds=2))] if not endpoint == "radio/np" else None,
|
||||
RateLimiter(times=25, seconds=2))] if not endpoint == "radio/np" else None,
|
||||
)
|
||||
|
||||
app.add_event_handler("startup", self.on_start)
|
||||
@@ -65,21 +66,22 @@ class Radio(FastAPI):
|
||||
await self.radio_util.load_playlists()
|
||||
|
||||
async def radio_skip(
|
||||
self, data: ValidRadioNextRequest, request: Request
|
||||
self, data: ValidRadioNextRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Skip to the next track in the queue, or to the UUID specified in `skipTo` if provided.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioNextRequest): Contains the API key, optional UUID to skip to, and station name.
|
||||
- **data** (ValidRadioNextRequest): Contains optional UUID to skip to, and station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Indicates success or failure of the skip operation.
|
||||
"""
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
try:
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if data.skipTo:
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.skipTo, data.station)
|
||||
if not queue_item:
|
||||
@@ -115,21 +117,22 @@ class Radio(FastAPI):
|
||||
raise e # Re-raise HTTPException
|
||||
|
||||
async def radio_reshuffle(
|
||||
self, data: ValidRadioReshuffleRequest, request: Request
|
||||
self, data: ValidRadioReshuffleRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Reshuffle the play queue.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioReshuffleRequest): Contains the API key and station name.
|
||||
- **data** (ValidRadioReshuffleRequest): Contains the station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Indicates success of the reshuffle operation.
|
||||
"""
|
||||
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
random.shuffle(self.radio_util.active_playlist[data.station])
|
||||
return JSONResponse(content={"ok": True})
|
||||
@@ -205,21 +208,22 @@ class Radio(FastAPI):
|
||||
return JSONResponse(content=out_json)
|
||||
|
||||
async def radio_queue_shift(
|
||||
self, data: ValidRadioQueueShiftRequest, request: Request
|
||||
self, data: ValidRadioQueueShiftRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Shift the position of a UUID within the queue.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioQueueShiftRequest): Contains the API key, UUID to shift, and station name.
|
||||
- **data** (ValidRadioQueueShiftRequest): Contains the UUID to shift, and station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Indicates success of the shift operation.
|
||||
"""
|
||||
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
||||
if not queue_item:
|
||||
@@ -242,21 +246,22 @@ class Radio(FastAPI):
|
||||
)
|
||||
|
||||
async def radio_queue_remove(
|
||||
self, data: ValidRadioQueueRemovalRequest, request: Request
|
||||
self, data: ValidRadioQueueRemovalRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Remove an item from the current play queue.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioQueueRemovalRequest): Contains the API key, UUID of the item to remove, and station name.
|
||||
- **data** (ValidRadioQueueRemovalRequest): Contains the UUID of the item to remove, and station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Indicates success of the removal operation.
|
||||
"""
|
||||
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
||||
if not queue_item:
|
||||
@@ -343,24 +348,27 @@ class Radio(FastAPI):
|
||||
data: ValidRadioNextRequest,
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
user=Depends(get_current_user),
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Get the next track in the queue. The track will be removed from the queue in the process.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioNextRequest): Contains the API key, optional UUID to skip to, and station name.
|
||||
- **data** (ValidRadioNextRequest): Contains optional UUID to skip to, and station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **background_tasks** (BackgroundTasks): Background tasks for webhook execution.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Contains the next track information.
|
||||
"""
|
||||
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
logging.info("Radio get next")
|
||||
if data.station not in self.radio_util.active_playlist.keys():
|
||||
raise HTTPException(status_code=500, detail="No such station/not ready")
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if (
|
||||
not isinstance(self.radio_util.active_playlist[data.station], list)
|
||||
or not self.radio_util.active_playlist[data.station]
|
||||
@@ -411,21 +419,23 @@ class Radio(FastAPI):
|
||||
return JSONResponse(content=next)
|
||||
|
||||
async def radio_request(
|
||||
self, data: ValidRadioSongRequest, request: Request
|
||||
self, data: ValidRadioSongRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Handle song requests.
|
||||
|
||||
Parameters:
|
||||
- **data** (ValidRadioSongRequest): Contains the API key, artist, song, and station name.
|
||||
- **data** (ValidRadioSongRequest): Contains artist, song, and station name.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Indicates success or failure of the request.
|
||||
"""
|
||||
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
artistsong: Optional[str] = data.artistsong
|
||||
artist: Optional[str] = data.artist
|
||||
song: Optional[str] = data.song
|
||||
@@ -454,7 +464,7 @@ class Radio(FastAPI):
|
||||
return JSONResponse(content={"result": search})
|
||||
|
||||
def radio_typeahead(
|
||||
self, data: ValidRadioTypeaheadRequest, request: Request
|
||||
self, data: ValidRadioTypeaheadRequest, request: Request, user=Depends(get_current_user)
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Handle typeahead queries for the radio.
|
||||
@@ -462,10 +472,14 @@ class Radio(FastAPI):
|
||||
Parameters:
|
||||
- **data** (ValidRadioTypeaheadRequest): Contains the typeahead query.
|
||||
- **request** (Request): The HTTP request object.
|
||||
- **user**: Current authenticated user.
|
||||
|
||||
Returns:
|
||||
- **JSONResponse**: Contains the typeahead results.
|
||||
"""
|
||||
if "dj" not in user.get("roles", []):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
if not isinstance(data.query, str):
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
|
Reference in New Issue
Block a user