Skip to content

Commit f21276a

Browse files
committed
Merge branch 'release/5.0.0'
2 parents 5ef2e85 + b2edf37 commit f21276a

File tree

10 files changed

+133
-51
lines changed

10 files changed

+133
-51
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. This projec
55

66
## Unreleased
77

8+
## [5.0.0] - 2025-12-01
9+
10+
### Changed
11+
12+
- [#298](https://github.com/laravel-json-api/laravel/pull/298)
13+
and [#70](https://github.com/laravel-json-api/laravel/issues/70) The authorizer implementation now allows methods to
14+
return either `bool` or an Illuminate Auth `Response`.
15+
- **BREAKING** The return type for the `authorizeResource()` method on both resource and query request classes has
16+
changed to `bool|Response` (where response is the Illuminate Auth response). If you are manually calling this method
17+
and relying on the return value being a boolean, this change is breaking. However, the vast majority of applications
18+
should be able to upgrade without any changes.
19+
820
## [4.1.1] - 2024-11-30
921

1022
### Fixed

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"require": {
2626
"php": "^8.2",
2727
"ext-json": "*",
28-
"laravel-json-api/core": "^4.3.2",
28+
"laravel-json-api/core": "^5.0.1",
2929
"laravel-json-api/eloquent": "^4.4",
3030
"laravel-json-api/encoder-neomerx": "^4.1",
3131
"laravel-json-api/exceptions": "^3.1",
@@ -53,7 +53,7 @@
5353
},
5454
"extra": {
5555
"branch-alias": {
56-
"dev-develop": "4.x-dev"
56+
"dev-develop": "5.x-dev"
5757
},
5858
"laravel": {
5959
"aliases": {

src/Http/Requests/FormRequest.php

+38-24
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace LaravelJsonApi\Laravel\Http\Requests;
1313

14+
use Illuminate\Auth\Access\AuthorizationException;
15+
use Illuminate\Auth\Access\Response;
1416
use Illuminate\Auth\AuthenticationException;
1517
use Illuminate\Contracts\Auth\Guard;
1618
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;
@@ -226,42 +228,54 @@ public function schema(): Schema
226228
*/
227229
protected function passesAuthorization()
228230
{
229-
/**
230-
* If the developer has implemented the `authorize` method, we
231-
* will return the result if it is a boolean. This allows
232-
* the developer to return a null value to indicate they want
233-
* the default authorization to run.
234-
*/
235-
if (method_exists($this, 'authorize')) {
236-
if (is_bool($passes = $this->container->call([$this, 'authorize']))) {
237-
return $passes;
231+
try {
232+
/**
233+
* If the developer has implemented the `authorize` method, we
234+
* will return the result if it is a boolean. This allows
235+
* the developer to return a null value to indicate they want
236+
* the default authorization to run.
237+
*/
238+
if (method_exists($this, 'authorize')) {
239+
$result = $this->container->call([$this, 'authorize']);
240+
if ($result !== null) {
241+
return $result instanceof Response ? $result->authorize() : $result;
242+
}
238243
}
239-
}
240244

241-
/**
242-
* If the developer has not authorized the request themselves,
243-
* we run our default authorization as long as authorization is
244-
* enabled for both the server and the schema (checked via the
245-
* `mustAuthorize()` method).
246-
*/
247-
if (method_exists($this, 'authorizeResource')) {
248-
return $this->container->call([$this, 'authorizeResource']);
249-
}
245+
/**
246+
* If the developer has not authorized the request themselves,
247+
* we run our default authorization as long as authorization is
248+
* enabled for both the server and the schema (checked via the
249+
* `mustAuthorize()` method).
250+
*/
251+
if (method_exists($this, 'authorizeResource')) {
252+
$result = $this->container->call([$this, 'authorizeResource']);
253+
return $result instanceof Response ? $result->authorize() : $result;
254+
}
250255

256+
} catch (AuthorizationException $ex) {
257+
$this->failIfUnauthenticated();
258+
throw $ex;
259+
}
251260
return true;
252261
}
253262

254-
/**
255-
* @inheritDoc
256-
*/
257-
protected function failedAuthorization()
263+
protected function failIfUnauthenticated()
258264
{
259-
/** @var Guard $auth */
265+
/** @var Guard $auth */
260266
$auth = $this->container->make(Guard::class);
261267

262268
if ($auth->guest()) {
263269
throw new AuthenticationException();
264270
}
271+
}
272+
273+
/**
274+
* @inheritDoc
275+
*/
276+
protected function failedAuthorization()
277+
{
278+
$this->failIfUnauthenticated();
265279

266280
parent::failedAuthorization();
267281
}

src/Http/Requests/ResourceQuery.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace LaravelJsonApi\Laravel\Http\Requests;
1313

14+
use Illuminate\Auth\Access\Response;
1415
use Illuminate\Contracts\Validation\Validator;
1516
use Illuminate\Database\Eloquent\Model;
1617
use LaravelJsonApi\Contracts\Auth\Authorizer;
@@ -104,9 +105,9 @@ public static function queryOne(string $resourceType): QueryParameters
104105
* Perform resource authorization.
105106
*
106107
* @param Authorizer $authorizer
107-
* @return bool
108+
* @return bool|Response
108109
*/
109-
public function authorizeResource(Authorizer $authorizer): bool
110+
public function authorizeResource(Authorizer $authorizer): bool|Response
110111
{
111112
if ($this->isViewingAny()) {
112113
return $authorizer->index(

src/Http/Requests/ResourceRequest.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace LaravelJsonApi\Laravel\Http\Requests;
1313

14+
use Illuminate\Auth\Access\Response;
1415
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
1516
use Illuminate\Contracts\Validation\Validator;
1617
use Illuminate\Database\Eloquent\Model;
@@ -150,9 +151,9 @@ public function toMany(): Collection
150151
* Perform resource authorization.
151152
*
152153
* @param Authorizer $authorizer
153-
* @return bool
154+
* @return bool|Response
154155
*/
155-
public function authorizeResource(Authorizer $authorizer): bool
156+
public function authorizeResource(Authorizer $authorizer): bool|Response
156157
{
157158
if ($this->isCreating()) {
158159
return $authorizer->store(

stubs/authorizer.stub

+21-20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace {{ namespace }};
44

5+
use Illuminate\Auth\Access\Response;
56
use Illuminate\Http\Request;
67
use LaravelJsonApi\Contracts\Auth\Authorizer;
78

@@ -13,9 +14,9 @@ class {{ class }} implements Authorizer
1314
*
1415
* @param Request $request
1516
* @param string $modelClass
16-
* @return bool
17+
* @return bool|Response
1718
*/
18-
public function index(Request $request, string $modelClass): bool
19+
public function index(Request $request, string $modelClass): bool|Response
1920
{
2021
// TODO: Implement index() method.
2122
}
@@ -25,9 +26,9 @@ class {{ class }} implements Authorizer
2526
*
2627
* @param Request $request
2728
* @param string $modelClass
28-
* @return bool
29+
* @return bool|Response
2930
*/
30-
public function store(Request $request, string $modelClass): bool
31+
public function store(Request $request, string $modelClass): bool|Response
3132
{
3233
// TODO: Implement store() method.
3334
}
@@ -37,9 +38,9 @@ class {{ class }} implements Authorizer
3738
*
3839
* @param Request $request
3940
* @param object $model
40-
* @return bool
41+
* @return bool|Response
4142
*/
42-
public function show(Request $request, object $model): bool
43+
public function show(Request $request, object $model): bool|Response
4344
{
4445
// TODO: Implement show() method.
4546
}
@@ -49,9 +50,9 @@ class {{ class }} implements Authorizer
4950
*
5051
* @param object $model
5152
* @param Request $request
52-
* @return bool
53+
* @return bool|Response
5354
*/
54-
public function update(Request $request, object $model): bool
55+
public function update(Request $request, object $model): bool|Response
5556
{
5657
// TODO: Implement update() method.
5758
}
@@ -61,9 +62,9 @@ class {{ class }} implements Authorizer
6162
*
6263
* @param Request $request
6364
* @param object $model
64-
* @return bool
65+
* @return bool|Response
6566
*/
66-
public function destroy(Request $request, object $model): bool
67+
public function destroy(Request $request, object $model): bool|Response
6768
{
6869
// TODO: Implement destroy() method.
6970
}
@@ -74,9 +75,9 @@ class {{ class }} implements Authorizer
7475
* @param Request $request
7576
* @param object $model
7677
* @param string $fieldName
77-
* @return bool
78+
* @return bool|Response
7879
*/
79-
public function showRelated(Request $request, object $model, string $fieldName): bool
80+
public function showRelated(Request $request, object $model, string $fieldName): bool|Response
8081
{
8182
// TODO: Implement showRelated() method.
8283
}
@@ -87,9 +88,9 @@ class {{ class }} implements Authorizer
8788
* @param Request $request
8889
* @param object $model
8990
* @param string $fieldName
90-
* @return bool
91+
* @return bool|Response
9192
*/
92-
public function showRelationship(Request $request, object $model, string $fieldName): bool
93+
public function showRelationship(Request $request, object $model, string $fieldName): bool|Response
9394
{
9495
// TODO: Implement showRelationship() method.
9596
}
@@ -100,9 +101,9 @@ class {{ class }} implements Authorizer
100101
* @param Request $request
101102
* @param object $model
102103
* @param string $fieldName
103-
* @return bool
104+
* @return bool|Response
104105
*/
105-
public function updateRelationship(Request $request, object $model, string $fieldName): bool
106+
public function updateRelationship(Request $request, object $model, string $fieldName): bool|Response
106107
{
107108
// TODO: Implement updateRelationship() method.
108109
}
@@ -113,9 +114,9 @@ class {{ class }} implements Authorizer
113114
* @param Request $request
114115
* @param object $model
115116
* @param string $fieldName
116-
* @return bool
117+
* @return bool|Response
117118
*/
118-
public function attachRelationship(Request $request, object $model, string $fieldName): bool
119+
public function attachRelationship(Request $request, object $model, string $fieldName): bool|Response
119120
{
120121
// TODO: Implement attachRelationship() method.
121122
}
@@ -126,9 +127,9 @@ class {{ class }} implements Authorizer
126127
* @param Request $request
127128
* @param object $model
128129
* @param string $fieldName
129-
* @return bool
130+
* @return bool|Response
130131
*/
131-
public function detachRelationship(Request $request, object $model, string $fieldName): bool
132+
public function detachRelationship(Request $request, object $model, string $fieldName): bool|Response
132133
{
133134
// TODO: Implement detachRelationship() method.
134135
}

tests/dummy/app/Http/Controllers/Api/V1/UserController.php

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
class UserController extends Controller
2020
{
2121
use Actions\FetchOne;
22+
use Actions\Destroy;
2223
use Actions\FetchRelated;
2324
use Actions\FetchRelationship;
2425
use Actions\UpdateRelationship;

tests/dummy/app/Policies/UserPolicy.php

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace App\Policies;
1313

1414
use App\Models\User;
15+
use Illuminate\Auth\Access\Response;
1516

1617
class UserPolicy
1718
{
@@ -50,4 +51,17 @@ public function updatePhone(User $user, User $other): bool
5051
{
5152
return $user->is($other);
5253
}
54+
55+
/**
56+
* Determine if the user can delete the other user.
57+
*
58+
* @param User $user
59+
* @param User $other
60+
* @return bool|Response
61+
*/
62+
public function delete(User $user, User $other)
63+
{
64+
return $user->is($other) ? true : Response::denyAsNotFound('not found message');
65+
}
66+
5367
}

tests/dummy/routes/api.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
});
2626

2727
/** Users */
28-
$server->resource('users')->only('show')->relationships(function ($relationships) {
28+
$server->resource('users')->only('show','destroy')->relationships(function ($relationships) {
2929
$relationships->hasOne('phone');
3030
})->actions(function ($actions) {
3131
$actions->get('me');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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\Users;
13+
14+
use App\Models\User;
15+
use App\Tests\Api\V1\TestCase;
16+
17+
class DeleteTest extends TestCase
18+
{
19+
20+
public function test(): void
21+
{
22+
$user = User::factory()->createOne();
23+
24+
$expected = $this->serializer
25+
->user($user);
26+
$response = $this
27+
->actingAs(User::factory()->createOne())
28+
->jsonApi('users')
29+
->delete(url('/api/v1/users', $expected['id']));
30+
31+
$response->assertNotFound()
32+
->assertHasError(404, [
33+
'detail' => 'not found message',
34+
'status' => '404',
35+
'title' => 'Not Found',
36+
]);
37+
}
38+
}

0 commit comments

Comments
 (0)