Sistem otorisasi sumber daya canggih di Laravel. Bagian 3. Atribut baca / tulis, model sendiri

pengantar


Halo, Khabrovchans sayang.

Hari ini saya membawa perhatian Anda pada bagian terakhir dari serangkaian artikel tentang otorisasi lanjutan atas tindakan dengan sumber daya di Laravel. Untuk lebih memahami apa yang akan dibahas dalam artikel ini, Anda perlu membaca bagian pertama dan kedua .


Kali ini, pernyataan masalah telah berubah sedikit: Kami sudah memiliki otorisasi tindakan dengan sumber daya dengan cara otorisasi metode pengendali yang sesuai. Tugasnya adalah untuk mengotorisasi modifikasi / tampilan bidang tertentu dari model. Anda juga perlu menyadari kemampuan untuk mengotorisasi pengeditan / melihat oleh penulis model Anda.


Materi ini dimaksudkan untuk melatih programmer, dan akan sulit bagi pengembang pemula untuk memahaminya.



Bagian 4. Otorisasi tindakan dengan atribut


Bagian teoretis


Untuk menerapkan fungsi ini, saya berencana untuk menggunakan sifat bawaan Laravel HasAttributes, HidesAttributes, GuardsAttributes . Dengan sifat-sifat ini, kerangka kerja mengontrol pengisian massa dan tampilan atribut. Saya berencana untuk meninggalkan kemampuan untuk secara paksa mengisi / membaca atribut, karena jika saya sepenuhnya mengotorisasi semua tindakan baca / tulis, sangat mungkin bahwa kami akan mengganggu kinerja program.


Untuk menyelesaikan tugas, kita perlu:


  • Ganti metode getHidden (), getVisible () untuk menyembunyikan bidang yang tidak diotorisasi.
  • Ganti metode isFillable () untuk melindungi dari rekaman bidang yang tidak sah.
  • 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
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("view-attr-$key-" . static::class);

        return $ability;
    }

    private function userCanUpdateAttribute(string $key): bool
    {
        /** @var User $user */
        $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). . , , β€” .


Kebijakan Umum Model Kebijakan


<?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;
    }
}

Saya juga mencatat bahwa untuk menghindari kesalahan saat runtime, ada baiknya memperkenalkan cek untuk ketersediaan metode pengguna () atau serupa dalam model target.


Itu saja untuk saat ini. Semoga artikel ini bermanfaat. Kami memiliki sistem otorisasi tindakan yang cukup lengkap dan fleksibel. Jika Anda memiliki pertanyaan, saya pasti akan menjawab. Dan tentu saja, komentar atau saran yang membangun juga diterima. Jika diinginkan, Anda dapat melihat implementasi proyek pelatihan ini secara lebih rinci di repositori .


All Articles