laravel-permissionを試す

# はじめに
Laravelでロールベースアクセス制御の割としっかりしたものを提供する必要があったので、
Laravelの拡張パッケージでお馴染みのSpatieが提供している[laravel-permission](https://github.com/spatie/laravel-permission)を試してみました。

何がどのように関係してくるのか毎回英語を読むのが怠いので残しておきます。

使ってるうちに知見が得られたら、なるべくここを更新します。
今のところは認可チェックする各ミドルウェアのテストコードを書いて動かして中身を理解したレベルです。

# 環境
PHP 7.2
Laravel 5.7
※動くことを確認した

# インストール

```bash
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"
```

テーブルがいくつか作られるので、モデルを理解しておく

![image.png](https://qiita-image-store.s3.amazonaws.com/0/56851/6f769d09-ee4d-83c5-47ec-17c4d2a2165f.png)

rolesとpermissionsのguard_nameには、
config/auth.phpで設定しているデフォルトのguardが入る。

複数のguardを設定したい場合の[ガイド](https://github.com/spatie/laravel-permission#using-multiple-guards)はあるけど、今回は必要ないので飛ばす。

model_typeにはEloquentのクラスが入る。
どうやらEloquentのポリモーフィックリレーションを使用して実装されていそうなことが判る。
（ソース見ると実際そうなってる）

## テーブルについて
### roles

#### 概要
役割のマスタテーブル
予め必要なものを登録しておく

登録するものの例：

* admin
* operator
* customer

など
※Super-adminとか定義できるみたいなので、その辺も念頭に置いて設定する

#### 登録方法

```php
$role = \Spatie\Permission\Models\Role::create(['name' => 'operator']);
```

### model_has_roles
#### 概要
モデルに対して役割を紐づける
userモデルに付与すれば、それが管理者とか運用者とかになる
部長・課長とか付けて役職と紐づけても良いと思う

#### 登録方法

```php
$user->assignRole('operator', 'customer');
```

'operator'とかの箇所はrole_idでも可。
メソッドの中で、stringかnumericかチェックして検索条件を切り替えてる

#### 解除方法

```php
$user->removeRole('read');
```

### permissions
#### 概要
許可設定のマスタテーブル
予め必要なものを登録しておく

登録するものの例：

* read
* write
* delete

など

#### 登録方法

```php
$permission = \Spatie\Permission\Models\Permission::create(['name' => 'read']);
```

### model_has_permissions
#### 概要
モデルに対して直接許可設定を付与する
userモデルに付与すれば、特権ユーザーみたいなものが作れる想定
（普通は管理者以上が触れる機能だけど、事務員さんに実際は入れてもらうので管理者じゃなくても権限付与したい、とかそういうヤツ）

#### 登録方法

```php
$user->givePermissionTo('read', 'write');
```

'read'とかの箇所はpermission_idでも可。
メソッドの中で、stringかnumericかチェックして検索条件を切り替えてる

#### 解除方法

```php
$user->revokePermissionTo('read');
```

### role_has_permissions
#### 概要
役割に対して許可設定を紐づけておくマスタテーブル

#### 登録方法
以下のどちらか

```php
$role->givePermissionTo($permission);
// or
$permission->assignRole($role);
```

まとめて登録したい場合は以下のどちらか使うとぐるぐるしなくて良い
（中でぐるぐるはしてるけど）

```php
$role->syncPermissions($permissions);
// or
$permission->syncRoles($roles);
```

#### 解除方法
役割に対する許可の設定を動的に変えるのがあまりイメージは湧かないけど以下でできる
（誤ったデータで作った場合に消したいとかそれくらいかな～と）

```php
$role->revokePermissionTo($permission);
// or
$permission->removeRole($role);
```

## 2019/01/23補足
各ミドルウェアの実装上は使われていない。
どんなときに使われるかチェックしたら、

> 「hasPermissionViaRoleでPermissionを渡すと、そのPermissionがRoleに存在するかチェックする」

だった。
Roleごとに細かい権限付与したい場合は必要だけど、
カジュアルに役割 or 権限でチェックかけたい場合には、
このテーブルを使う必要はないですね。

### 最初に何が必要か
以下のテーブルについて、システムの初期セットアップで必要
（メンテナンス画面作ってちまちま入れても良いけど）

* roles
* permissions
* role_has_permissions

## マイグレーション実行する
テーブルのことが判ったところで、マイグレーション実行

```bash
php artisan migrate
```

この後はSeederとか使ってテキトーなデータを以下のテーブルに突っ込んでおく
* roles
* permissions
* role_has_permissions

# 使い方
## ユーザー登録
（めんどくさいので割愛）
User::createして戻り値で取ったuserをassignRoleかgivePermissionToする

## コードに入れる
### middleware
middlewareに設定したいのでmiddleware使う

以下を足す

```php:App/Http/Kernel.php
protected $routeMiddleware = [
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
```

上記の実装見れば判るけど、Laravelからすると特殊な構成の認証ユーザーの持ち方をしている場合は、
自前のクラスを作って差し替えれば、思い通りになる。
（実装自体は簡単過ぎるので書かない）

```php:route.php

// rolesのデータでだけチェック
Route::group(['middleware' => ['role:admin|operator']], function () {
    //
});

// permissionsのデータでだけチェック
Route::group(['middleware' => ['permission:read|write']], function () {
    //
});

// roles or permissionsのデータに居るかをチェック
Route::group(['middleware' => ['role_or_permission:operator|read']], function () {
    //
});
```

role_has_permissionってどこで使われるんだ？？

### blade
使わないけど残しておく

```php
@role('operator')
@endrole

@hasallroles('writer|admin')
@else
@endhasallroles
```

permission周りは他のと同じで`@can`を使うらしい

# おわりに
大体理解した気がする。
業務システムに必要なところも実装されてそうだし良さげ。

今回の要件に合致すれば使おうかな。

技術的な課題としてはVueでどうやって連携させるかかなー。
メニュー周りくらい＆SPAで書いてるわけじゃないから、bladeに直接vueコンポーネント書いて回避するようにする気がする。