Handle authenticated room instance takeover
CI/CD / test_and_check_app (push) Failing after 2m7s
CI/CD / Publish immutable image (push) Skipped

This commit is contained in:
2026-09-10 16:33:13 +02:00
parent 2ab95cebdf
commit 640a2970c3
18 changed files with 351 additions and 22 deletions
@@ -8,6 +8,7 @@ use App\Http\Resources\RoomResource;
use App\Models\Room;
use App\Services\Rooms\RoomPageService;
use App\Services\Rooms\RoomParticipationService;
use App\Services\Rooms\RoomPlayerInstanceService;
use Inertia\Inertia;
use Inertia\Response;
@@ -16,6 +17,7 @@ class EmbeddedRoomController extends Controller
public function __construct(
private readonly RoomParticipationService $roomParticipation,
private readonly RoomPageService $roomPage,
private readonly RoomPlayerInstanceService $playerInstances,
) {}
public function __invoke(ShowEmbeddedRoomRequest $request): Response
@@ -33,6 +35,11 @@ class EmbeddedRoomController extends Controller
$validated['player_name'],
$request->session()->get("room_players.{$room->id}"),
);
$player = $this->roomParticipation->activateInstance(
$room,
$player,
$this->playerInstances->for($request),
);
$request->session()->put("room_players.{$room->id}", $player->id);
+5
View File
@@ -10,6 +10,7 @@ use App\Models\Player;
use App\Models\Room;
use App\Services\Rooms\RoomCreationService;
use App\Services\Rooms\RoomPageService;
use App\Services\Rooms\RoomPlayerInstanceService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
@@ -20,6 +21,7 @@ class RoomController extends Controller
public function __construct(
private readonly RoomCreationService $roomCreation,
private readonly RoomPageService $roomPage,
private readonly RoomPlayerInstanceService $playerInstances,
) {}
public function create(Request $request): Response
@@ -40,6 +42,9 @@ class RoomController extends Controller
$validated,
$request->user(),
);
$player->update([
'active_instance_id' => $this->playerInstances->for($request),
]);
$request->session()->put(
"room_players.{$room->id}",
@@ -4,12 +4,14 @@ namespace App\Http\Controllers;
use App\Http\Requests\JoinRoomRequest;
use App\Services\Rooms\RoomParticipationService;
use App\Services\Rooms\RoomPlayerInstanceService;
use Illuminate\Http\RedirectResponse;
class RoomPlayerController extends Controller
{
public function __construct(
private readonly RoomParticipationService $roomParticipation,
private readonly RoomPlayerInstanceService $playerInstances,
) {}
public function join(JoinRoomRequest $request): RedirectResponse
@@ -23,6 +25,11 @@ class RoomPlayerController extends Controller
$request->user(),
is_array($sessionPlayerIds) ? $sessionPlayerIds : [],
);
$player = $this->roomParticipation->activateInstance(
$room,
$player,
$this->playerInstances->for($request),
);
$request->session()->put(
"room_players.{$room->id}",
+44 -2
View File
@@ -3,6 +3,8 @@
namespace App\Http\Middleware;
use App\Models\Room;
use App\Services\Rooms\RoomParticipationService;
use App\Services\Rooms\RoomPlayerInstanceService;
use Closure;
use Illuminate\Http\Request;
use Inertia\Inertia;
@@ -10,6 +12,11 @@ use Symfony\Component\HttpFoundation\Response;
class EnsureRoomParticipant
{
public function __construct(
private readonly RoomParticipationService $roomParticipation,
private readonly RoomPlayerInstanceService $playerInstances,
) {}
/**
* Handle an incoming request.
*
@@ -44,13 +51,43 @@ class EnsureRoomParticipant
}
$playerId = $request->session()->get($sessionKey);
$hasSessionPlayer = is_numeric($playerId);
$instanceId = $this->playerInstances->for($request);
$player = is_numeric($playerId)
$player = $hasSessionPlayer
? $room->players()->find((int) $playerId)
: null;
if ($player === null && $playerId) {
if ($player === null && $hasSessionPlayer) {
$request->session()->forget($sessionKey);
return to_route('room.join', [
'join_code' => $room->join_code,
])->withErrors([
'join_code' => 'Diese Spieler-Instanz ist nicht mehr aktiv.',
]);
}
if (
$player !== null
&& $player->active_instance_id !== null
&& $player->active_instance_id !== $instanceId
) {
$request->session()->forget($sessionKey);
return to_route('room.join', [
'join_code' => $room->join_code,
])->withErrors([
'join_code' => 'Dein Account ist diesem Raum in einer anderen Instanz beigetreten.',
]);
}
if ($player !== null) {
$player = $this->roomParticipation->activateInstance(
$room,
$player,
$instanceId,
);
}
if ($player === null && $request->user() !== null) {
@@ -59,6 +96,11 @@ class EnsureRoomParticipant
->first();
if ($player !== null) {
$player = $this->roomParticipation->activateInstance(
$room,
$player,
$instanceId,
);
$request->session()->put($sessionKey, $player->id);
}
}
+19 -6
View File
@@ -13,6 +13,8 @@ use Inertia\Inertia;
class ShowEmbeddedRoomRequest extends FormRequest
{
private const string PLAYER_NAME_TAKEN_MESSAGE = 'Dieser Anzeigename ist in dem Raum bereits vorhanden.';
/**
* Determine if the user is authorized to make this request.
*/
@@ -72,16 +74,18 @@ class ShowEmbeddedRoomRequest extends FormRequest
}
$sessionPlayerId = $this->session()->get("room_players.{$room->id}");
$existingPlayer = is_numeric($sessionPlayerId)
? $room->players()->find((int) $sessionPlayerId)
: null;
$existingPlayer = null;
if ($existingPlayer === null && $this->user() !== null) {
if ($this->user() !== null) {
$existingPlayer = $room->players()
->where('user_id', $this->user()->id)
->first();
}
if ($existingPlayer === null && is_numeric($sessionPlayerId)) {
$existingPlayer = $room->players()->find((int) $sessionPlayerId);
}
$nameIsTaken = Player::query()
->where('room_id', $room->id)
->where('name', $this->input('player_name'))
@@ -94,7 +98,7 @@ class ShowEmbeddedRoomRequest extends FormRequest
if ($nameIsTaken) {
$validator->errors()->add(
'player_name',
'Dieser Anzeigename ist in dem Raum bereits vorhanden.',
self::PLAYER_NAME_TAKEN_MESSAGE,
);
}
},
@@ -112,9 +116,18 @@ class ShowEmbeddedRoomRequest extends FormRequest
protected function failedValidation(Validator $validator): never
{
$message = $validator->errors()->first();
$showLoginAction = $this->user() === null
&& $message === self::PLAYER_NAME_TAKEN_MESSAGE;
if ($showLoginAction) {
$this->session()->put('url.intended', $this->fullUrl());
}
throw new HttpResponseException(
Inertia::render('embedded/Error', [
'message' => $validator->errors()->first(),
'message' => $message,
'showLoginAction' => $showLoginAction,
])->toResponse($this)->setStatusCode(422),
);
}
@@ -19,6 +19,7 @@ class CurrentPlayerResource extends JsonResource
return [
'id' => $player->id,
'instance_id' => $player->active_instance_id,
'name' => $player->name,
'is_moderator' => $player->is_moderator,
'current_vote' => $pageData->currentVote,