Restrict document edit/delete to owners and close public registration
Adds DocumentPolicy gating update/delete to the creator (admins bypass via before()), invokes $this->authorize() in DocumentEditor mount/save/delete, applies can:update,document on the edit route, hides the edit button for non-owners, and removes the open /register routes so accounts must be provisioned via the admin panel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,8 @@ class DocumentEditor extends Component
|
|||||||
public function mount(?Document $document = null)
|
public function mount(?Document $document = null)
|
||||||
{
|
{
|
||||||
if ($document) {
|
if ($document) {
|
||||||
|
$this->authorize('update', $document);
|
||||||
|
|
||||||
$this->document = $document;
|
$this->document = $document;
|
||||||
$this->title = $document->title;
|
$this->title = $document->title;
|
||||||
$this->content = $document->content;
|
$this->content = $document->content;
|
||||||
@@ -40,6 +42,8 @@ public function save(DocumentService $documentService)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if ($this->isEditMode && $this->document) {
|
if ($this->isEditMode && $this->document) {
|
||||||
|
$this->authorize('update', $this->document);
|
||||||
|
|
||||||
$this->document = $documentService->updateDocument(
|
$this->document = $documentService->updateDocument(
|
||||||
$this->document,
|
$this->document,
|
||||||
$this->title,
|
$this->title,
|
||||||
@@ -71,6 +75,8 @@ public function delete(DocumentService $documentService)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->authorize('delete', $this->document);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$documentService->deleteDocument($this->document);
|
$documentService->deleteDocument($this->document);
|
||||||
session()->flash('message', 'Document deleted successfully!');
|
session()->flash('message', 'Document deleted successfully!');
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class DocumentPolicy
|
||||||
|
{
|
||||||
|
public function before(User $user): ?bool
|
||||||
|
{
|
||||||
|
return $user->isAdmin() ? true : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(User $user, Document $document): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user, Document $document): bool
|
||||||
|
{
|
||||||
|
return $document->created_by === $user->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, Document $document): bool
|
||||||
|
{
|
||||||
|
return $document->created_by === $user->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
{{ $document->title }}
|
{{ $document->title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@auth
|
@can('update', $document)
|
||||||
<a
|
<a
|
||||||
href="{{ route('documents.edit', $document) }}"
|
href="{{ route('documents.edit', $document) }}"
|
||||||
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 whitespace-nowrap"
|
class="inline-flex items-center justify-center px-3 sm:px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 whitespace-nowrap"
|
||||||
@@ -16,7 +16,7 @@ class="inline-flex items-center justify-center px-3 sm:px-4 py-2 bg-indigo-600 t
|
|||||||
</svg>
|
</svg>
|
||||||
{{ __('messages.documents.edit') }}
|
{{ __('messages.documents.edit') }}
|
||||||
</a>
|
</a>
|
||||||
@endauth
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center text-xs sm:text-sm text-gray-500 gap-2 sm:gap-4">
|
<div class="flex flex-col sm:flex-row sm:items-center text-xs sm:text-sm text-gray-500 gap-2 sm:gap-4">
|
||||||
|
|||||||
@@ -7,16 +7,10 @@
|
|||||||
use App\Http\Controllers\Auth\NewPasswordController;
|
use App\Http\Controllers\Auth\NewPasswordController;
|
||||||
use App\Http\Controllers\Auth\PasswordController;
|
use App\Http\Controllers\Auth\PasswordController;
|
||||||
use App\Http\Controllers\Auth\PasswordResetLinkController;
|
use App\Http\Controllers\Auth\PasswordResetLinkController;
|
||||||
use App\Http\Controllers\Auth\RegisteredUserController;
|
|
||||||
use App\Http\Controllers\Auth\VerifyEmailController;
|
use App\Http\Controllers\Auth\VerifyEmailController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::middleware('guest')->group(function () {
|
Route::middleware('guest')->group(function () {
|
||||||
Route::get('register', [RegisteredUserController::class, 'create'])
|
|
||||||
->name('register');
|
|
||||||
|
|
||||||
Route::post('register', [RegisteredUserController::class, 'store']);
|
|
||||||
|
|
||||||
Route::get('login', [AuthenticatedSessionController::class, 'create'])
|
Route::get('login', [AuthenticatedSessionController::class, 'create'])
|
||||||
->name('login');
|
->name('login');
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -44,7 +44,9 @@
|
|||||||
// 認証が必要なルート(より具体的なルートを先に定義)
|
// 認証が必要なルート(より具体的なルートを先に定義)
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
Route::get('/create', DocumentEditor::class)->name('create');
|
Route::get('/create', DocumentEditor::class)->name('create');
|
||||||
Route::get('/{document}/edit', DocumentEditor::class)->name('edit');
|
Route::get('/{document}/edit', DocumentEditor::class)
|
||||||
|
->middleware('can:update,document')
|
||||||
|
->name('edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 公開ルート(動的ルートは最後に)
|
// 公開ルート(動的ルートは最後に)
|
||||||
|
|||||||
Reference in New Issue
Block a user