mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
fix: external entries should be read-only (#733)
This commit is contained in:
@@ -95,7 +95,6 @@ ELASTIC_SCOUT_DRIVER_REFRESH_DOCUMENTS=false
|
||||
ELASTIC_MIGRATIONS_TABLE=elastic_migrations
|
||||
|
||||
# external profile
|
||||
PROFILE_MAX_ENTRIES=1000
|
||||
USER_MAX_PROFILES=5
|
||||
|
||||
# ffmpeg
|
||||
|
||||
@@ -10,6 +10,4 @@ namespace App\Constants\Config;
|
||||
class ExternalProfileConstants
|
||||
{
|
||||
final public const MAX_PROFILES_QUALIFIED = 'externalprofile.user_max_profiles';
|
||||
|
||||
final public const MAX_ENTRIES_QUALIFIED = 'externalprofile.profile_max_entries';
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ use App\Filament\Components\Fields\BelongsTo;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\CreateExternalEntry;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\EditExternalEntry;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\ListExternalEntries;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\ViewExternalEntry;
|
||||
use App\Filament\Resources\List\External\RelationManagers\ExternalEntryExternalProfileRelationManager;
|
||||
@@ -327,9 +325,7 @@ class ExternalEntry extends BaseResource
|
||||
{
|
||||
return [
|
||||
'index' => ListExternalEntries::route('/'),
|
||||
'create' => CreateExternalEntry::route('/create'),
|
||||
'view' => ViewExternalEntry::route('/{record:entry_id}'),
|
||||
'edit' => EditExternalEntry::route('/{record:entry_id}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseCreateResource;
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class CreateExternalEntry.
|
||||
*/
|
||||
class CreateExternalEntry extends BaseCreateResource
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
use App\Filament\Resources\Base\BaseEditResource;
|
||||
|
||||
/**
|
||||
* Class EditExternalEntry.
|
||||
*/
|
||||
class EditExternalEntry extends BaseEditResource
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -23,9 +23,6 @@ class ListExternalEntries extends BaseListResources
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,29 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\List\External;
|
||||
|
||||
use App\Actions\Http\Api\DestroyAction;
|
||||
use App\Actions\Http\Api\ForceDeleteAction;
|
||||
use App\Actions\Http\Api\IndexAction;
|
||||
use App\Actions\Http\Api\RestoreAction;
|
||||
use App\Actions\Http\Api\ShowAction;
|
||||
use App\Actions\Http\Api\StoreAction;
|
||||
use App\Actions\Http\Api\UpdateAction;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Controllers\Api\BaseController;
|
||||
use App\Http\Middleware\Api\EnabledOnlyOnLocalhost;
|
||||
use App\Http\Middleware\Models\List\ExternalProfileExceedsEntryLimit;
|
||||
use App\Http\Requests\Api\IndexRequest;
|
||||
use App\Http\Requests\Api\ShowRequest;
|
||||
use App\Http\Requests\Api\StoreRequest;
|
||||
use App\Http\Requests\Api\UpdateRequest;
|
||||
use App\Http\Resources\List\External\Collection\ExternalEntryCollection;
|
||||
use App\Http\Resources\List\External\Resource\ExternalEntryResource;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryController.
|
||||
@@ -40,14 +28,7 @@ class ExternalEntryController extends BaseController
|
||||
{
|
||||
parent::__construct(ExternalEntry::class, 'externalentry,externalprofile');
|
||||
|
||||
$isExternalProfileManagementAllowed = Str::of(EnsureFeaturesAreActive::class)
|
||||
->append(':')
|
||||
->append(AllowExternalProfileManagement::class)
|
||||
->__toString();
|
||||
|
||||
$this->middleware(EnabledOnlyOnLocalhost::class);
|
||||
$this->middleware($isExternalProfileManagementAllowed)->except(['index', 'show']);
|
||||
$this->middleware(ExternalProfileExceedsEntryLimit::class)->only(['store', 'restore']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,26 +50,6 @@ class ExternalEntryController extends BaseController
|
||||
return new ExternalEntryCollection($resources, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param StoreRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param StoreAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function store(StoreRequest $request, ExternalProfile $externalprofile, StoreAction $action): ExternalEntryResource
|
||||
{
|
||||
$validated = array_merge(
|
||||
$request->validated(),
|
||||
[ExternalEntry::ATTRIBUTE_PROFILE => $externalprofile->getKey()]
|
||||
);
|
||||
|
||||
$externalentry = $action->store(ExternalEntry::query(), $validated);
|
||||
|
||||
return new ExternalEntryResource($externalentry, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
@@ -106,67 +67,4 @@ class ExternalEntryController extends BaseController
|
||||
|
||||
return new ExternalEntryResource($show, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param UpdateRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param UpdateAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function update(UpdateRequest $request, ExternalProfile $externalprofile, ExternalEntry $externalentry, UpdateAction $action): ExternalEntryResource
|
||||
{
|
||||
$updated = $action->update($externalentry, $request->validated());
|
||||
|
||||
return new ExternalEntryResource($updated, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param DestroyAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function destroy(ExternalProfile $externalprofile, ExternalEntry $externalentry, DestroyAction $action): ExternalEntryResource
|
||||
{
|
||||
$deleted = $action->destroy($externalentry);
|
||||
|
||||
return new ExternalEntryResource($deleted, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param RestoreAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function restore(ExternalProfile $externalprofile, ExternalEntry $externalentry, RestoreAction $action): ExternalEntryResource
|
||||
{
|
||||
$restored = $action->restore($externalentry);
|
||||
|
||||
return new ExternalEntryResource($restored, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param ForceDeleteAction $action
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function forceDelete(ExternalProfile $externalprofile, ExternalEntry $externalentry, ForceDeleteAction $action): JsonResponse
|
||||
{
|
||||
$message = $action->forceDelete($externalentry);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware\Models\List;
|
||||
|
||||
use App\Constants\Config\ExternalProfileConstants;
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileExceedsEntryLimit.
|
||||
*/
|
||||
class ExternalProfileExceedsEntryLimit
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure(Request): mixed $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$entryLimit = intval(Config::get(ExternalProfileConstants::MAX_ENTRIES_QUALIFIED));
|
||||
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $request->user('sanctum');
|
||||
|
||||
if (
|
||||
intval($profile?->externalentries()?->count()) >= $entryLimit
|
||||
&& empty($user?->can(SpecialPermission::BYPASS_FEATURE_FLAGS->value))
|
||||
) {
|
||||
abort(403, "External profiles cannot contain more than '$entryLimit' entries.");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace App\Policies\List\External;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Enums\Auth\ExtendedCrudPermission;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
@@ -78,7 +77,7 @@ class ExternalEntryPolicy extends BasePolicy
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = request()->route('externalprofile');
|
||||
|
||||
return $user->getKey() === $profile?->user_id;
|
||||
return parent::create($user) && $user->getKey() === $profile?->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +98,7 @@ class ExternalEntryPolicy extends BasePolicy
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = request()->route('externalprofile');
|
||||
|
||||
return !$entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(CrudPermission::UPDATE->format(ExternalEntry::class));
|
||||
return parent::update($user, $entry) && $user->getKey() === $profile?->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +119,7 @@ class ExternalEntryPolicy extends BasePolicy
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = request()->route('externalprofile');
|
||||
|
||||
return !$entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(CrudPermission::DELETE->format(ExternalEntry::class));
|
||||
return parent::delete($user, $entry) && $user->getKey() === $profile?->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,6 +140,6 @@ class ExternalEntryPolicy extends BasePolicy
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = request()->route('externalprofile');
|
||||
|
||||
return $entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(ExtendedCrudPermission::RESTORE->format(ExternalEntry::class));
|
||||
return parent::restore($user, $entry) && $user->getKey() === $profile?->user_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,8 @@ return [
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These values represent caps on external profiles to prevent spam. By default,
|
||||
| an individual external profile is permitted 1000 entries, and a user
|
||||
| is permitted 5 external profiles.
|
||||
|
|
||||
| a user is permitted 5 external profiles.
|
||||
*/
|
||||
|
||||
'profile_max_entries' => (int) env('PROFILE_MAX_ENTRIES', 1000),
|
||||
|
||||
'user_max_profiles' => (int) env('USER_MAX_PROFILES', 5),
|
||||
];
|
||||
|
||||
@@ -68,7 +68,7 @@ class PermissionSeeder extends Seeder
|
||||
$this->registerResource(DiscordThread::class, CrudPermission::cases());
|
||||
|
||||
// List Resources
|
||||
$this->registerResource(ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->registerResource(ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->registerResource(ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->registerResource(Playlist::class, $extendedCrudPermissions);
|
||||
$this->registerResource(PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -75,7 +75,7 @@ class AdminSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, CrudPermission::cases());
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -58,7 +58,7 @@ class DeveloperRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::VIEW]);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -58,7 +58,7 @@ class EncoderRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::CREATE, CrudPermission::UPDATE, CrudPermission::VIEW]);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -58,7 +58,7 @@ class PatronRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::VIEW]);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -38,7 +38,7 @@ class PlaylistUserRoleSeeder extends RoleSeeder
|
||||
);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -58,7 +58,7 @@ class WikiEditorRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::VIEW]);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -58,7 +58,7 @@ class WikiViewerRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::VIEW]);
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalEntry::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Auth\Permission;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Class DeletePermissionsSeeder.
|
||||
*/
|
||||
class DeletePermissionsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Permission::query()
|
||||
->orWhere(Permission::ATTRIBUTE_NAME, 'create external entry')
|
||||
->orWhere(Permission::ATTRIBUTE_NAME, 'update external entry')
|
||||
->orWhere(Permission::ATTRIBUTE_NAME, 'delete external entry')
|
||||
->orWhere(Permission::ATTRIBUTE_NAME, 'force delete external entry')
|
||||
->orWhere(Permission::ATTRIBUTE_NAME, 'restore external entry')
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user