Skip to content

Commit f84bd21

Browse files
Gregory Haddowlindyhopchris
Gregory Haddow
authored andcommitted
fix: authorizer response should be honoured on destroy action when no request class for resource
1 parent 1b81420 commit f84bd21

File tree

4 files changed

+93
-3
lines changed

4 files changed

+93
-3
lines changed

src/Http/Controllers/Actions/Destroy.php

+15-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
1313

1414
use Illuminate\Auth\Access\AuthorizationException;
15+
use Illuminate\Auth\Access\Response as AuthResponse;
1516
use Illuminate\Auth\AuthenticationException;
1617
use Illuminate\Contracts\Support\Responsable;
1718
use Illuminate\Http\Response;
@@ -63,13 +64,24 @@ public function destroy(Route $route, StoreContract $store)
6364
* So we need to trigger authorization in this case.
6465
*/
6566
if (!$request) {
66-
$check = $route->authorizer()->destroy(
67+
$result = $route->authorizer()->destroy(
6768
$request = \request(),
6869
$model,
6970
);
7071

71-
throw_if(false === $check && Auth::guest(), new AuthenticationException());
72-
throw_if(false === $check, new AuthorizationException());
72+
if ($result instanceof AuthResponse) {
73+
try {
74+
$result->authorize();
75+
} catch (AuthorizationException $ex) {
76+
if (!$ex->hasStatus()) {
77+
throw_if(Auth::guest(), new AuthenticationException());
78+
}
79+
throw $ex;
80+
}
81+
}
82+
83+
throw_if(false === $result && Auth::guest(), new AuthenticationException());
84+
throw_if(false === $result, new AuthorizationException());
7385
}
7486

7587
$response = null;
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Policies;
6+
7+
use App\Models\Tag;
8+
use App\Models\User;
9+
use Illuminate\Auth\Access\Response;
10+
11+
class TagPolicy
12+
{
13+
14+
/**
15+
* Determine if the user can delete the tag
16+
*
17+
* @param ?User $user
18+
* @param Tag $tag
19+
* @return bool|Response
20+
*/
21+
public function delete(?User $user, Tag $tag)
22+
{
23+
return Response::denyAsNotFound('not found message');
24+
}
25+
}

tests/dummy/routes/api.php

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
use LaravelJsonApi\Laravel\Facades\JsonApiRoute;
11+
use LaravelJsonApi\Laravel\Http\Controllers\JsonApiController;
1112

1213
JsonApiRoute::server('v1')
1314
->prefix('v1')
@@ -35,4 +36,6 @@
3536
$server->resource('videos')->relationships(function ($relationships) {
3637
$relationships->hasMany('tags');
3738
});
39+
40+
$server->resource('tags', '\\' . JsonApiController::class)->only('destroy');
3841
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/*
3+
* Copyright 2024 Cloud Creativity Limited
4+
*
5+
* Use of this source code is governed by an MIT-style
6+
* license that can be found in the LICENSE file or at
7+
* https://opensource.org/licenses/MIT.
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace App\Tests\Api\V1\Tags;
13+
14+
use App\Models\Tag;
15+
use App\Models\User;
16+
use App\Tests\Api\V1\TestCase;
17+
18+
class DeleteTest extends TestCase
19+
{
20+
public function test(): void
21+
{
22+
$tag = Tag::factory()->createOne();
23+
24+
$response = $this
25+
->actingAs(User::factory()->createOne())
26+
->jsonApi('users')
27+
->delete(url('/api/v1/tags', $tag));
28+
29+
$response->assertNotFound()->assertErrorStatus([
30+
'detail' => 'not found message',
31+
'status' => '404',
32+
'title' => 'Not Found',
33+
]);
34+
}
35+
36+
public function testUnauthenticated(): void
37+
{
38+
$tag = Tag::factory()->createOne();
39+
40+
$response = $this
41+
->jsonApi('users')
42+
->delete(url('/api/v1/tags', $tag));
43+
44+
$response->assertNotFound()->assertErrorStatus([
45+
'detail' => 'not found message',
46+
'status' => '404',
47+
'title' => 'Not Found',
48+
]);
49+
}
50+
}

0 commit comments

Comments
 (0)