Skip to content

Commit d6cdb82

Browse files
committed
Support custom gate denial messages in authorization
Refactored authorization logic to use Gate::inspect and return Response objects, allowing custom denial messages from policies. Updated Response handling to extract messages or allowed status for gates, and added a test and policy to verify custom messages are returned for denied actions.
1 parent 7bcaba0 commit d6cdb82

File tree

4 files changed

+144
-9
lines changed

4 files changed

+144
-9
lines changed

src/Concerns/Authorizable.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ public function authorizeTo($ability, $model)
5858
* @param string $ability
5959
* @param Model|string $model
6060
*
61-
* @return bool
61+
* @return Response
6262
*/
6363
public function authorizedTo($ability, $model)
6464
{
6565
if ($this->isAuthorizingEnabled()) {
6666
$resolver = function () use ($ability, $model) {
67-
return Gate::check($ability, $model);
67+
return Gate::inspect($ability, $model);
6868
};
6969

7070
if ($this->isAuthorizationCacheEnabled()) {

src/Http/Response.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,17 @@ public function resource(Resource $resource)
3232

3333
protected function buildGatesForModel(Model $model, Resource $resource, array $gates)
3434
{
35+
$authorizedToView = $resource->authorizedTo('view', $model);
36+
$authorizedToUpdate = $resource->authorizedTo('update', $model);
37+
$authorizedToDelete = $resource->authorizedTo('delete', $model);
38+
$authorizedToRestore = $resource->authorizedTo('restore', $model);
39+
$authorizedToForceDelete = $resource->authorizedTo('forceDelete', $model);
3540
return array_merge(
36-
in_array('view', $gates) ? [config('rest.gates.names.authorized_to_view') => $resource->authorizedTo('view', $model)] : [],
37-
in_array('update', $gates) ? [config('rest.gates.names.authorized_to_update') => $resource->authorizedTo('update', $model)] : [],
38-
in_array('delete', $gates) ? [config('rest.gates.names.authorized_to_delete') => $resource->authorizedTo('delete', $model)] : [],
39-
in_array('restore', $gates) ? [config('rest.gates.names.authorized_to_restore') => $resource->authorizedTo('restore', $model)] : [],
40-
in_array('forceDelete', $gates) ? [config('rest.gates.names.authorized_to_force_delete') => $resource->authorizedTo('forceDelete', $model)] : [],
41+
in_array('view', $gates) ? [config('rest.gates.names.authorized_to_view') => $authorizedToView->message() ?? $authorizedToView->allowed()] : [],
42+
in_array('update', $gates) ? [config('rest.gates.names.authorized_to_update') => $authorizedToUpdate->message() ?? $authorizedToUpdate->allowed()] : [],
43+
in_array('delete', $gates) ? [config('rest.gates.names.authorized_to_delete') => $authorizedToDelete->message() ?? $authorizedToDelete->allowed()] : [],
44+
in_array('restore', $gates) ? [config('rest.gates.names.authorized_to_restore') => $authorizedToRestore->message() ?? $authorizedToRestore->allowed()] : [],
45+
in_array('forceDelete', $gates) ? [config('rest.gates.names.authorized_to_force_delete') => $authorizedToForceDelete->message() ?? $authorizedToForceDelete->allowed()] : [],
4146
);
4247
}
4348

@@ -126,15 +131,19 @@ public function modelToResponse(Model $model, Resource $resource, array $request
126131
public function toResponse($request)
127132
{
128133
if ($this->responsable instanceof LengthAwarePaginator) {
134+
if ($this->resource->isGatingEnabled() && in_array('create', $request->input('search.gates', []))) {
135+
$authorizedToCreate = $this->resource->authorizedTo('create', $this->resource::newModel()::class);
136+
}
137+
129138
$restLengthAwarePaginator = new \Lomkit\Rest\Pagination\LengthAwarePaginator(
130139
$this->responsable->items(),
131140
$this->responsable->total(),
132141
$this->responsable->perPage(),
133142
$this->responsable->currentPage(),
134143
$this->responsable->getOptions(),
135-
$this->resource->isGatingEnabled() && in_array('create', $request->input('search.gates', [])) ? [
144+
isset($authorizedToCreate) ? [
136145
config('rest.gates.key') => [
137-
config('rest.gates.names.authorized_to_create') => $this->resource->authorizedTo('create', $this->resource::newModel()::class),
146+
config('rest.gates.names.authorized_to_create') => $authorizedToCreate->message() ?? $authorizedToCreate->allowed(),
138147
],
139148
] : []
140149
);

tests/Feature/Controllers/AutomaticGatingTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Lomkit\Rest\Tests\Support\Policies\DeletePolicy;
1616
use Lomkit\Rest\Tests\Support\Policies\ForceDeletePolicy;
1717
use Lomkit\Rest\Tests\Support\Policies\GreenPolicy;
18+
use Lomkit\Rest\Tests\Support\Policies\RedPolicyWithMessage;
1819
use Lomkit\Rest\Tests\Support\Policies\RestorePolicy;
1920
use Lomkit\Rest\Tests\Support\Policies\UpdatePolicy;
2021
use Lomkit\Rest\Tests\Support\Policies\ViewPolicy;
@@ -62,6 +63,44 @@ public function test_searching_automatic_gated_resource(): void
6263
);
6364
}
6465

66+
public function test_searching_automatic_gated_resource_and_custom_message(): void
67+
{
68+
$model = ModelFactory::new()
69+
->create();
70+
71+
Gate::policy(Model::class, RedPolicyWithMessage::class);
72+
73+
$response = $this->post(
74+
'/api/automatic-gating/search',
75+
[
76+
'search' => [
77+
'gates' => ['create', 'view', 'update', 'delete', 'forceDelete', 'restore'],
78+
],
79+
],
80+
['Accept' => 'application/json']
81+
);
82+
83+
$this->assertResourcePaginated(
84+
$response,
85+
[$model],
86+
new AutomaticGatingResource(),
87+
[
88+
[
89+
'gates' => [
90+
'authorized_to_view' => 'You don\'t have permission to view user',
91+
'authorized_to_update' => 'You don\'t have permission to update user',
92+
'authorized_to_delete' => 'You don\'t have permission to delete user',
93+
'authorized_to_restore' => 'You don\'t have permission to restore user',
94+
'authorized_to_force_delete' => 'You don\'t have permission to force delete user',
95+
],
96+
],
97+
]
98+
);
99+
$response->assertJson(
100+
['meta' => ['gates' => ['authorized_to_create' => true]]]
101+
);
102+
}
103+
65104
public function test_searching_automatic_gated_resource_with_global_config_disabled(): void
66105
{
67106
$model = ModelFactory::new()
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Lomkit\Rest\Tests\Support\Policies;
4+
5+
use Illuminate\Auth\Access\HandlesAuthorization;
6+
use Illuminate\Auth\Access\Response;
7+
use Illuminate\Database\Eloquent\Model;
8+
9+
class RedPolicyWithMessage
10+
{
11+
use HandlesAuthorization;
12+
13+
/**
14+
* Determine whether the user can view the list of models.
15+
*
16+
* @param $user
17+
*/
18+
public function viewAny($user)
19+
{
20+
return true;
21+
}
22+
23+
/**
24+
* Determine whether the user can view the model.
25+
*
26+
* @param $user
27+
* @param Model $model
28+
*/
29+
public function view($user, Model $model)
30+
{
31+
return Response::deny('You don\'t have permission to view user');
32+
}
33+
34+
/**
35+
* Determine whether the user can create models.
36+
*
37+
* @param $user
38+
*/
39+
public function create($user)
40+
{
41+
return Response::deny('You don\'t have permission to create user');
42+
}
43+
44+
/**
45+
* Determine whether the user can update the model.
46+
*
47+
* @param $user
48+
* @param Model $model
49+
*/
50+
public function update($user, Model $model)
51+
{
52+
return Response::deny('You don\'t have permission to update user');
53+
}
54+
55+
/**
56+
* Determine whether the user can delete the model.
57+
*
58+
* @param $user
59+
* @param Model $model
60+
*/
61+
public function delete($user, Model $model)
62+
{
63+
return Response::deny('You don\'t have permission to delete user');
64+
}
65+
66+
/**
67+
* Determine whether the user can restore the model.
68+
*
69+
* @param $user
70+
* @param Model $model
71+
*/
72+
public function restore($user, Model $model)
73+
{
74+
return Response::deny('You don\'t have permission to restore user');
75+
}
76+
77+
/**
78+
* Determine whether the user can permanently delete the model.
79+
*
80+
* @param $user
81+
* @param Model $model
82+
*/
83+
public function forceDelete($user, Model $model)
84+
{
85+
return Response::deny('You don\'t have permission to force delete user');
86+
}
87+
}

0 commit comments

Comments
 (0)