Skip to content

Commit e8d4e19

Browse files
Gregory Haddowlindyhopchris
authored andcommitted
feat: support auth responses from authorizer contract
1 parent d4a21ac commit e8d4e19

File tree

8 files changed

+99
-30
lines changed

8 files changed

+99
-30
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
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": "^4.3.2|^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",

src/Http/Requests/FormRequest.php

Lines changed: 38 additions & 24 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 3 additions & 2 deletions
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(

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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');
Lines changed: 38 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy