Introdução
Olá queridos Khabrovchans.
Hoje, chamo a atenção para a parte final de uma série de artigos sobre autorização avançada de ações com recursos no Laravel. Para entender melhor o que será discutido neste artigo, você precisa ler a primeira e a segunda partes.
Desta vez, a declaração do problema mudou um pouco: já temos autorização de ações com recursos por meio da autorização de métodos dos controladores correspondentes. A tarefa é autorizar a modificação / visualização de campos específicos do modelo. Você também precisa perceber a capacidade de autorizar a edição / visualização pelo autor de seus modelos.
Este material destina-se à prática de programadores e será difícil para um desenvolvedor iniciante entender.
Parte 4. Autorização de ações com atributos
Parte teórica
Para implementar essa funcionalidade, planejo usar as características internas do Laravel HasAttributes, HidesAttributes, GuardsAttributes . Com essas características, a estrutura controla o preenchimento em massa e a exibição de atributos. Pretendo deixar a capacidade de preencher / ler com força os atributos, porque se eu autorizar totalmente todas as ações de leitura / gravação, é muito provável que interrompamos o desempenho do programa.
Para realizar a tarefa, precisamos:
- Substitua os métodos getHidden (), getVisible () para ocultar campos não autorizados.
- Substitua o método isFillable () para proteger contra a gravação não autorizada de campos.
- authUpdate() .
- authView() .
- totallyGuarded() .
— , , . . , . - Banana Monkey Jungle, . / — . .
Laravel :
- $visible
- $hidden
- $guarded
- $fillable
. $visible . — $guarded . — , .
// , . app/Extensions/Traits, app/Extensions/Traits/GuardedModel. , ( ).
authView()( ) authUpdate()( ), , .
getVisible() getHidden().
, . filterVisibility(), , () .
filterVisibility() $hidden makeHidden(), , userCanViewAttribute($key) . makeVisible()
isFillable($key), , userCanUpdateAttribute($key)
userCanUpdateAttribute($key) userCanViewAttribute($key) . $user->can() spatie/laravel-permission. , , .
GuardedModel
<?php
namespace App\Extensions\Traits;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
trait GuardedModel
{
protected function authView(): array
{
return [];
}
protected function authUpdate(): array
{
return [];
}
public function getHidden(): array
{
$this->filterVisibility();
return parent::getHidden();
}
public function getVisible(): array
{
$this->filterVisibility();
return parent::getVisible();
}
public function isFillable($key)
{
if (in_array($key, $this->authView())) {
return $this->userCanUpdateAttribute($key);
}
return parent::isFillable($key);
}
private function filterVisibility(): void
{
$this->makeHidden($this->authView());
$authVisible = array_filter(
$this->authView(),
fn ($attr) => $this->userCanViewAttribute($attr)
);
$this->makeVisible($authVisible);
}
private function userCanViewAttribute(string $key): bool
{
$user = auth()->user();
$ability = !empty($user) && $user->can("view-attr-$key-" . static::class);
return $ability;
}
private function userCanUpdateAttribute(string $key): bool
{
$user = auth()->user();
$ability = !empty($user) && $user->can("update-attr-$key-" . static::class);
return $ability;
}
public function totallyGuarded()
{
$guarded = (
count($this->getFillable()) === 0
&& count($this->authView()) === 0
&& $this->getGuarded() == ['*']
);
return $guarded;
}
}
totallyGuarded(). Illuminate\Database\Eloquent\MassAssignmentException , . — , .
$user->can(«update-attr-$key-». static::class), . , attr. /.
, authView() authUpdate(). user user_id. user_id, user user_id . . , , $guarded, $hidden, $fillable, .
Posts
<?php
namespace App\Models;
use App\Extensions\Traits\GuardedModel;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use GuardedModel;
protected $hidden = ['id'];
protected $guarded = ['created_at', 'updated_at'];
protected $fillable = ['title', 'description', 'user_id'];
protected function authView(): array
{
return ['user_id', 'user'];
}
protected function authUpdate(): array
{
return ['user_id'];
}
public function user()
{
return $this->belongsTo(User::class);
}
}
5.
. user(), user_id. . , . $user->can('delete-self-'. $this->getModelClass()). , . isOwner(User $user, Model $model). . , , — .
Geral ModelPolicy Política
<?php
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;
abstract class ModelPolicy
{
use HandlesAuthorization;
abstract protected function getModelClass(): string;
public function delete(User $user, Model $model)
{
if ($user->can('delete-' . $this->getModelClass())) {
return true;
}
if ($user->can('delete-self-' . $this->getModelClass())) {
return $this->isOwner($user, $model);
}
return false;
}
private function isOwner(User $user, Model $model): bool
{
if (!empty($user) && method_exists($model, 'user')) {
return $user->getKey() === $model->getRelation('user')->getKey();
}
return false;
}
}
Também observo que, para evitar erros no tempo de execução, vale a pena introduzir uma verificação da disponibilidade do método user () ou similar no modelo de destino.
É tudo por agora. Espero que este artigo tenha sido útil. Temos um sistema bastante completo e flexível de autorização de ações. Se você tiver dúvidas, eu definitivamente responderei. E, claro, comentários ou sugestões construtivas também são bem-vindos. Se desejar, você pode ver a implementação deste projeto de treinamento em mais detalhes no repositório .