CakePHP3でUsersテーブルにroleカラムを追加し、管理者と一般利用者で権限を分けたい

「このページは管理者画面だから、一般ユーザーはアクセス禁止」

「このページは普通の機能画面だから、全ユーザーがアクセスできるようにしたい」

上記みたいな状況ってありますよね。

そういう時はいくつか方法があります。

その中でも、「Usersテーブルにroleカラムを追加し、管理者と一般利用者で権限を分ける」という方法を説明します。

CakePHP3でのやり方です。

【自分の環境】

macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

まずはroleカラムを追加

roleカラムを追加するマイグレーションからやっていきましょう。

(ターミナル)

./bin/cake bake migration AddRoleToUsers

これでマイグレーションファイルができます。

マイグレーションファイル)

<?php
use Migrations\AbstractMigration;

class AddRoleToUsers extends AbstractMigration
{
    public function up()
    {
        $table = $this->table('users');
        $table->addColumn('role', 'string', [
            'default' => 'user',
            'limit' => 255,
            'null' => false,
        ]);
        $table->update();
    }
    public function down()
    {
        $table = $this->table('users');
        $table->removeColumn('role');
        $table->update();
    }
}

カラム変更のマイグレーションなので、念のためロールバックできるようにup()とdown()に分けて書きます。

元々の値、デフォルトではuser(一般ユーザー)と入るようにします。

ほとんどのユーザーは一般ユーザーだからです。

文字数が長すぎるとか思う場合は「'limit' => 255」を調整してください。

これでマイグレーション実行します。

./bin/cake migrations migrate

DBを見るとroleカラムができていると思います。

DBをいじって管理者ユーザー(admin)を作る

DBをいじってroleに入っている値が「admin」のユーザーを作ります。

自分はphpMyAdminなので簡単に値の操作ができました。

このユーザーはどのページでもアクセスできるようにしていきます。

それぞれのコントローラにisAuthorizedファンクションの内容を書いていく

例えば私のサービスではUsersControllerでは「loginアクションとlogoutアクションには全ユーザーアクセスできるようにしたいが、indexアクションなどは管理画面として使用している」という状況があります。

この場合、loginアクションとlogoutアクションはadminでもuserでもアクセス可能で、その他のアクションはadminだけアクセス可能としたいです。

ただloginアクションに関しては明示的にアクセス可能とすると、ログイン機能がうまく働かなくなるので、ここでは一般ユーザーはlogoutアクションだけアクセス可能にしていきます。

※ややこしいですが、そういう仕様みたいです。

UsersControllerの一番下に、以下のようにisAuthorizedファンクションを記述します。

    // 認証時のロールチェック
    public function isAuthorized($user = null){

        // 役割がadminだったら全アクションにアクセス可能
        if($user['role'] === 'admin'){
            return true;
        }

        // 役割がuserだったらログアウトだけアクセス可能
        if($user['role'] === 'user'){    
            if(in_array($this->request->getParam('action'),array('logout'))){
                return true;
            }
        }
        
        // それ以外だったらfalseを返す
        return false;
    }

このように書きます。

すると、コメント通りですが、役割がadminだったら全アクションにアクセス可能で、役割がuserだったらログアウト画面だけアクセス可能になります。

ただ、まだやることはあります。

findAuthファンクションにて$this->Auth->user()で取得できる値にroleカラムを増やす

この操作をやらないと、isAuthorizedファンクションで上手くroleを判定できないです。

現在、UsersTable.php(Usersテーブル)のfindAuthファンクションはこのようになっているかと思います。

public function findAuth(\Cake\ORM\Query $query, array $options)
    {
        $query
            ->select(['id', 'name', 'mail', 'gender', 'pass']) // roleがない
            ->where(['Users.deleted IS' => NULL]);

        return $query;
    }

selectの引数に'role'を加えてあげてください。

public function findAuth(\Cake\ORM\Query $query, array $options)
    {
        $query
            ->select(['id', 'name', 'mail', 'role', 'gender', 'pass']) // roleを加えた
            ->where(['Users.deleted IS' => NULL]);

        return $query;
    }

このようにすると動くようになると思います。

指定したコントローラの管理者画面にはadminしか入れず、指定したコントローラ下で許可したlogout画面には全ユーザーがアクセスできます。

終わりに

いかがだったでしょうか。

自分のサービスではこのように実装しています。

少し長い説明になりました。

不明点があればコメントいただければと思います。

これ以外にもroute.phpで分けるなどのやり方もあるようです。

ありがとうございました。