Upload beautify
This commit is contained in:
46
app/Http/Requests/Artworks/ArtworkCreateRequest.php
Normal file
46
app/Http/Requests/Artworks/ArtworkCreateRequest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Artworks;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ArtworkCreateRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! $this->user()) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'required|string|max:150',
|
||||
'description' => 'nullable|string',
|
||||
'category' => 'nullable|string|max:120',
|
||||
'tags' => 'nullable|string|max:200',
|
||||
'license' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Artwork create unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
28
app/Http/Requests/Artworks/ArtworkTagsStoreRequest.php
Normal file
28
app/Http/Requests/Artworks/ArtworkTagsStoreRequest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Artworks;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ArtworkTagsStoreRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! $this->user()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'tags' => 'required|array|max:15',
|
||||
'tags.*' => 'required|string|max:64',
|
||||
];
|
||||
}
|
||||
}
|
||||
28
app/Http/Requests/Artworks/ArtworkTagsUpdateRequest.php
Normal file
28
app/Http/Requests/Artworks/ArtworkTagsUpdateRequest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Artworks;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ArtworkTagsUpdateRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! $this->user()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'tags' => 'required|array|max:15',
|
||||
'tags.*' => 'required|string|max:64',
|
||||
];
|
||||
}
|
||||
}
|
||||
68
app/Http/Requests/Dashboard/ArtworkDestroyRequest.php
Normal file
68
app/Http/Requests/Dashboard/ArtworkDestroyRequest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Dashboard;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ArtworkDestroyRequest extends FormRequest
|
||||
{
|
||||
private ?Artwork $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = Artwork::query()->whereKey($id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function artwork(): Artwork
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Dashboard artwork delete unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
68
app/Http/Requests/Dashboard/ArtworkEditRequest.php
Normal file
68
app/Http/Requests/Dashboard/ArtworkEditRequest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Dashboard;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ArtworkEditRequest extends FormRequest
|
||||
{
|
||||
private ?Artwork $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = Artwork::query()->whereKey($id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function artwork(): Artwork
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Dashboard artwork edit unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,36 @@
|
||||
|
||||
namespace App\Http\Requests\Dashboard;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class UpdateArtworkRequest extends FormRequest
|
||||
{
|
||||
private ?Artwork $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Authorization is enforced in the controller via ArtworkPolicy.
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = Artwork::query()->whereKey($id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,4 +44,28 @@ class UpdateArtworkRequest extends FormRequest
|
||||
'file' => 'nullable|image|max:102400',
|
||||
];
|
||||
}
|
||||
|
||||
public function artwork(): Artwork
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Dashboard artwork update unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
68
app/Http/Requests/Manage/ManageArtworkDestroyRequest.php
Normal file
68
app/Http/Requests/Manage/ManageArtworkDestroyRequest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Manage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ManageArtworkDestroyRequest extends FormRequest
|
||||
{
|
||||
private ?object $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = DB::table('artworks')->where('id', $id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function artwork(): object
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Manage artwork delete unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
68
app/Http/Requests/Manage/ManageArtworkEditRequest.php
Normal file
68
app/Http/Requests/Manage/ManageArtworkEditRequest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Manage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ManageArtworkEditRequest extends FormRequest
|
||||
{
|
||||
private ?object $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = DB::table('artworks')->where('id', $id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function artwork(): object
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Manage artwork edit unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
74
app/Http/Requests/Manage/ManageArtworkUpdateRequest.php
Normal file
74
app/Http/Requests/Manage/ManageArtworkUpdateRequest.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Manage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class ManageArtworkUpdateRequest extends FormRequest
|
||||
{
|
||||
private ?object $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$id = (int) $this->route('id');
|
||||
if ($id <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = DB::table('artworks')->where('id', $id)->first();
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'section' => 'nullable|integer',
|
||||
'description' => 'nullable|string',
|
||||
'artwork' => 'nullable|file|image',
|
||||
'attachment' => 'nullable|file',
|
||||
];
|
||||
}
|
||||
|
||||
public function artwork(): object
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Manage artwork update unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'artwork_id' => $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class ProfileUpdateRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'username' => ['sometimes', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
@@ -25,6 +25,21 @@ class ProfileUpdateRequest extends FormRequest
|
||||
'max:255',
|
||||
Rule::unique(User::class)->ignore($this->user()->id),
|
||||
],
|
||||
'name' => ['nullable', 'string', 'max:255'],
|
||||
'web' => ['nullable', 'url', 'max:255'],
|
||||
'day' => ['nullable', 'numeric', 'between:1,31'],
|
||||
'month' => ['nullable', 'numeric', 'between:1,12'],
|
||||
'year' => ['nullable', 'numeric', 'digits:4'],
|
||||
'gender' => ['nullable', 'in:m,f,n,M,F,N,X,x'],
|
||||
'country' => ['nullable', 'string', 'max:10'],
|
||||
'mailing' => ['nullable', 'boolean'],
|
||||
'notify' => ['nullable', 'boolean'],
|
||||
'about' => ['nullable', 'string'],
|
||||
'signature' => ['nullable', 'string'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'avatar' => ['nullable', 'image', 'max:2048', 'mimes:jpg,jpeg,png,webp'],
|
||||
'emoticon' => ['nullable', 'image', 'max:2048', 'mimes:jpg,jpeg,png,webp'],
|
||||
'photo' => ['nullable', 'image', 'max:2048', 'mimes:jpg,jpeg,png,webp'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
27
app/Http/Requests/Tags/PopularTagsRequest.php
Normal file
27
app/Http/Requests/Tags/PopularTagsRequest.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Tags;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class PopularTagsRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! $this->user()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'limit' => 'nullable|integer|min:1|max:50',
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Http/Requests/Tags/TagSearchRequest.php
Normal file
27
app/Http/Requests/Tags/TagSearchRequest.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Tags;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class TagSearchRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! $this->user()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'q' => 'nullable|string|max:64',
|
||||
];
|
||||
}
|
||||
}
|
||||
83
app/Http/Requests/Uploads/UploadCancelRequest.php
Normal file
83
app/Http/Requests/Uploads/UploadCancelRequest.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Uploads;
|
||||
|
||||
use App\Repositories\Uploads\UploadSessionRepository;
|
||||
use App\Services\Uploads\UploadTokenService;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class UploadCancelRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessionId = (string) $this->input('session_id');
|
||||
if ($sessionId === '') {
|
||||
$this->logUnauthorized('missing_session_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$token = $this->header('X-Upload-Token') ?: $this->input('upload_token');
|
||||
if (! $token) {
|
||||
$this->logUnauthorized('missing_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessions = $this->container->make(UploadSessionRepository::class);
|
||||
$session = $sessions->get($sessionId);
|
||||
if (! $session || $session->userId !== $user->id) {
|
||||
$this->logUnauthorized('not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$tokens = $this->container->make(UploadTokenService::class);
|
||||
$payload = $tokens->get((string) $token);
|
||||
if (! $payload) {
|
||||
$this->logUnauthorized('invalid_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if (($payload['session_id'] ?? null) !== $sessionId) {
|
||||
$this->logUnauthorized('token_session_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if ((int) ($payload['user_id'] ?? 0) !== (int) $user->id) {
|
||||
$this->logUnauthorized('token_user_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'session_id' => 'required|uuid',
|
||||
'upload_token' => 'nullable|string|min:40|max:200',
|
||||
];
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Upload cancel unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'session_id' => (string) $this->input('session_id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
app/Http/Requests/Uploads/UploadChunkRequest.php
Normal file
91
app/Http/Requests/Uploads/UploadChunkRequest.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Uploads;
|
||||
|
||||
use App\Repositories\Uploads\UploadSessionRepository;
|
||||
use App\Services\Uploads\UploadTokenService;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class UploadChunkRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessionId = (string) $this->input('session_id');
|
||||
if ($sessionId === '') {
|
||||
$this->logUnauthorized('missing_session_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$token = $this->header('X-Upload-Token') ?: $this->input('upload_token');
|
||||
if (! $token) {
|
||||
$this->logUnauthorized('missing_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessions = $this->container->make(UploadSessionRepository::class);
|
||||
$session = $sessions->get($sessionId);
|
||||
if (! $session || $session->userId !== $user->id) {
|
||||
$this->logUnauthorized('not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$tokens = $this->container->make(UploadTokenService::class);
|
||||
$payload = $tokens->get((string) $token);
|
||||
if (! $payload) {
|
||||
$this->logUnauthorized('invalid_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if (($payload['session_id'] ?? null) !== $sessionId) {
|
||||
$this->logUnauthorized('token_session_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if ((int) ($payload['user_id'] ?? 0) !== (int) $user->id) {
|
||||
$this->logUnauthorized('token_user_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$maxBytes = (int) config('uploads.chunk.max_bytes', 0);
|
||||
$maxKb = $maxBytes > 0 ? (int) ceil($maxBytes / 1024) : 5120;
|
||||
$chunkSizeRule = $maxBytes > 0 ? 'required|integer|min:1|max:' . $maxBytes : 'required|integer|min:1';
|
||||
|
||||
return [
|
||||
'session_id' => 'required|uuid',
|
||||
'offset' => 'required|integer|min:0',
|
||||
'total_size' => 'required|integer|min:1',
|
||||
'chunk_size' => $chunkSizeRule,
|
||||
'chunk' => 'required|file|max:' . $maxKb,
|
||||
'upload_token' => 'nullable|string|min:40|max:200',
|
||||
];
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Upload chunk unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'session_id' => (string) $this->input('session_id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
108
app/Http/Requests/Uploads/UploadFinishRequest.php
Normal file
108
app/Http/Requests/Uploads/UploadFinishRequest.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Uploads;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Repositories\Uploads\UploadSessionRepository;
|
||||
use App\Services\Uploads\UploadTokenService;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class UploadFinishRequest extends FormRequest
|
||||
{
|
||||
private ?Artwork $artwork = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessionId = (string) $this->input('session_id');
|
||||
if ($sessionId === '') {
|
||||
$this->logUnauthorized('missing_session_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessions = $this->container->make(UploadSessionRepository::class);
|
||||
$session = $sessions->get($sessionId);
|
||||
if (! $session || $session->userId !== $user->id) {
|
||||
$this->logUnauthorized('not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$token = $this->header('X-Upload-Token') ?: $this->input('upload_token');
|
||||
if ($token) {
|
||||
$tokens = $this->container->make(UploadTokenService::class);
|
||||
$payload = $tokens->get((string) $token);
|
||||
if (! $payload) {
|
||||
$this->logUnauthorized('invalid_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if (($payload['session_id'] ?? null) !== $sessionId) {
|
||||
$this->logUnauthorized('token_session_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if ((int) ($payload['user_id'] ?? 0) !== (int) $user->id) {
|
||||
$this->logUnauthorized('token_user_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
$artworkId = (int) $this->input('artwork_id');
|
||||
if ($artworkId <= 0) {
|
||||
$this->logUnauthorized('missing_artwork_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$artwork = Artwork::query()->find($artworkId);
|
||||
if (! $artwork || (int) $artwork->user_id !== (int) $user->id) {
|
||||
$this->logUnauthorized('artwork_not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$this->artwork = $artwork;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'session_id' => 'required|uuid',
|
||||
'artwork_id' => 'required|integer',
|
||||
'upload_token' => 'nullable|string|min:40|max:200',
|
||||
];
|
||||
}
|
||||
|
||||
public function artwork(): Artwork
|
||||
{
|
||||
if (! $this->artwork) {
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->artwork;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Upload finish unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'session_id' => (string) $this->input('session_id'),
|
||||
'artwork_id' => $this->input('artwork_id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
22
app/Http/Requests/Uploads/UploadInitRequest.php
Normal file
22
app/Http/Requests/Uploads/UploadInitRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Uploads;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class UploadInitRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return (bool) $this->user();
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'client' => 'nullable|string|max:64',
|
||||
];
|
||||
}
|
||||
}
|
||||
87
app/Http/Requests/Uploads/UploadStatusRequest.php
Normal file
87
app/Http/Requests/Uploads/UploadStatusRequest.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Uploads;
|
||||
|
||||
use App\Repositories\Uploads\UploadSessionRepository;
|
||||
use App\Services\Uploads\UploadTokenService;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final class UploadStatusRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
if (! $user) {
|
||||
$this->logUnauthorized('missing_user');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessionId = (string) $this->route('id');
|
||||
if ($sessionId === '') {
|
||||
$this->logUnauthorized('missing_session_id');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$sessions = $this->container->make(UploadSessionRepository::class);
|
||||
$session = $sessions->get($sessionId);
|
||||
if (! $session || $session->userId !== $user->id) {
|
||||
$this->logUnauthorized('not_owned_or_missing');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
$token = $this->header('X-Upload-Token') ?: $this->input('upload_token');
|
||||
if ($token) {
|
||||
$tokens = $this->container->make(UploadTokenService::class);
|
||||
$payload = $tokens->get((string) $token);
|
||||
if (! $payload) {
|
||||
$this->logUnauthorized('invalid_token');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if (($payload['session_id'] ?? null) !== $sessionId) {
|
||||
$this->logUnauthorized('token_session_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
|
||||
if ((int) ($payload['user_id'] ?? 0) !== (int) $user->id) {
|
||||
$this->logUnauthorized('token_user_mismatch');
|
||||
$this->denyAsNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function denyAsNotFound(): void
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
private function logUnauthorized(string $reason): void
|
||||
{
|
||||
logger()->warning('Upload status unauthorized access', [
|
||||
'reason' => $reason,
|
||||
'session_id' => (string) $this->route('id'),
|
||||
'user_id' => $this->user()?->id,
|
||||
'ip' => $this->ip(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'id' => 'required|uuid',
|
||||
'upload_token' => 'nullable|string|min:40|max:200',
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'id' => $this->route('id'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user