scope text endpoints by ownership

TextRepository gains findByUser; JsonTextRepository and the
fake implement filtering by stored userId. TextController
splits the list endpoint into getMyTexts (own) and
getAllTexts (admin), and getText now requires the session
user, returning 403 to non-owners while admins bypass.
This commit is contained in:
Yisroel Baum 2026-05-02 21:42:51 +03:00
parent ea6d65a77d
commit acdf703d80
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
4 changed files with 107 additions and 3 deletions

View file

@ -5,6 +5,7 @@ namespace App\Text;
use App\Text\Text;
use App\Text\CreateTextDto;
use App\Text\TextRepository;
use App\User\User;
use App\User\UserRepository;
use DomainException;
@ -67,6 +68,28 @@ class JsonTextRepository implements TextRepository
);
}
/**
* @return Text[]
*/
public function findByUser(User $user): array
{
$texts = $this->readTexts();
$userId = $user->getId();
$owned = array_filter(
$texts,
function (array $data) use ($userId) {
return $data['userId'] === $userId;
}
);
return array_map(
function (array $data) {
return $this->hydrate($data);
},
array_values($owned)
);
}
private function hydrate(array $data): Text
{
$user = $this->userRepo->find($data['userId']);

View file

@ -16,7 +16,7 @@ class TextController
private TextRepository $textRepository,
) {}
public function getTexts(Response $response): Response
public function getAllTexts(Response $response): Response
{
$texts = $this->textRepository->getAll();
@ -31,14 +31,63 @@ class TextController
return $response->withHeader('Content-Type', 'application/json');
}
public function getText(Response $response, int $textId): Response
{
public function getMyTexts(
Request $request,
Response $response,
): Response {
$user = $request->getAttribute('user');
if (!$user instanceof User) {
return $this->errorResponse(
$response,
401,
'unauthenticated'
);
}
$texts = $this->textRepository->findByUser($user);
$data = array_map(function ($text) {
return [
'id' => $text->getId(),
'name' => $text->getName(),
];
}, $texts);
$response->getBody()->write(json_encode($data));
return $response->withHeader('Content-Type', 'application/json');
}
public function getText(
Request $request,
Response $response,
int $textId,
): Response {
$user = $request->getAttribute('user');
if (!$user instanceof User) {
return $this->errorResponse(
$response,
401,
'unauthenticated'
);
}
$text = $this->textRepository->find($textId);
if ($text === null) {
return $response->withStatus(404);
}
if (
$text->getUser()->getId() !== $user->getId()
&& !$user->isAdmin()
) {
return $this->errorResponse(
$response,
403,
'forbidden'
);
}
$response->getBody()->write(json_encode([
'id' => $text->getId(),
'name' => $text->getName(),

View file

@ -4,6 +4,7 @@ namespace App\Text;
use App\Text\Text;
use App\Text\CreateTextDto;
use App\User\User;
interface TextRepository
{
@ -15,4 +16,9 @@ interface TextRepository
* @return Text[]
*/
public function getAll(): array;
/**
* @return Text[]
*/
public function findByUser(User $user): array;
}