diff --git a/backend/bracket/routes/util.py b/backend/bracket/routes/util.py index b37d7762..477f0592 100644 --- a/backend/bracket/routes/util.py +++ b/backend/bracket/routes/util.py @@ -47,17 +47,17 @@ async def round_with_matches_dependency( async def stage_dependency(tournament_id: TournamentId, stage_id: StageId) -> StageWithStageItems: - stages = await get_full_tournament_details( + stages_result = await get_full_tournament_details( tournament_id, no_draft_rounds=False, stage_id=stage_id ) - if len(stages) < 1: + if len(stages_result) < 1: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Could not find stage with id {stage_id}", ) - return stages[0] + return stages_result[0] async def stage_item_dependency( diff --git a/backend/tests/integration_tests/api/teams_test.py b/backend/tests/integration_tests/api/teams_test.py index 9713822f..231de839 100644 --- a/backend/tests/integration_tests/api/teams_test.py +++ b/backend/tests/integration_tests/api/teams_test.py @@ -5,11 +5,19 @@ import pytest from bracket.database import database from bracket.models.db.team import Team from bracket.utils.db import fetch_one_parsed_certain -from bracket.utils.dummy_records import DUMMY_MOCK_TIME, DUMMY_TEAM1 +from bracket.utils.dummy_records import DUMMY_MOCK_TIME, DUMMY_TEAM1, DUMMY_TOURNAMENT from bracket.utils.http import HTTPMethod -from tests.integration_tests.api.shared import SUCCESS_RESPONSE, send_tournament_request +from tests.integration_tests.api.shared import ( + SUCCESS_RESPONSE, + send_auth_request, + send_tournament_request, +) from tests.integration_tests.models import AuthContext -from tests.integration_tests.sql import assert_row_count_and_clear, inserted_team +from tests.integration_tests.sql import ( + assert_row_count_and_clear, + inserted_team, + inserted_tournament, +) @pytest.mark.asyncio(loop_scope="session") @@ -152,3 +160,25 @@ async def test_team_upload_and_remove_logo( assert not await aiofiles.os.path.exists( f"static/team-logos/{response['data']['logo_path']}" ) + + +@pytest.mark.asyncio(loop_scope="session") +async def test_cross_tournament_team_access_denied( + startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext +) -> None: + """Regression test: a team from tournament B cannot be accessed via tournament A's URL.""" + async with inserted_tournament( + DUMMY_TOURNAMENT.model_copy( + update={"club_id": auth_context.club.id, "dashboard_endpoint": None} + ) + ) as other_tournament: + async with inserted_team( + DUMMY_TEAM1.model_copy(update={"tournament_id": other_tournament.id}) + ) as other_team: + response = await send_auth_request( + HTTPMethod.PUT, + f"tournaments/{auth_context.tournament.id}/teams/{other_team.id}", + auth_context, + json={"name": "Hacked", "active": True, "player_ids": []}, + ) + assert response.get("detail") == f"Could not find team with id {other_team.id}"