PHPで認可、権限管理(その1、ZfcRbacのサンプルを動かす)
はじめに
今まで、権限管理(認証から認可処理)をするときには、ZF1 の Zend_Aclで作っていた。設定箇所が多い実装をしていたので、もうちょっと何か無いかな、と感じていた。今回、仕事でまたまた要り用が出てきた。→ 他のライブラリを試してみることにした。
予備知識
- その1:「認証」と「認可」の違い。私は徳丸本で知りました。http://bookpub.jp/books/bp/144
- その2:ロールベースアクセス制御。これは初耳でした。業務システムの人からすると常識らしい。
権限管理のサンプルユースケース
- 某ホームページの管理をしていると仮定。
- 外部のライターさんが、ホームページ内の一部の枠を更新できるようなCMSツールを作る(よくある)。
- ライターさんは下書きで登録して、ホームページの担当者が、事前に記事をチェックしてからProduction環境に公開する、という業務フローがある(よくある)。
ロール | Create | Read | Update | Delete |
---|---|---|---|---|
システム管理者 | ○ | ○ | ○ | ○ |
担当者 | × | ○ | × | × |
ロール |
Create (下書きまで) |
Read |
Update (下書きまで) |
Delete (下書きまで) |
---|---|---|---|---|
A社のライターさん |
○ (A社記事のみ) |
○ (A社記事のみ) |
○ (A社記事のみ) |
○ (A社記事のみ) |
ロールには継承関係がある。
システム管理者 > 担当者 > A社ライターさん・B社ライターさん
環境
- PHP5.4。今回は、BEAR.Saturday にのせる形にしたい。
ライブラリ
今回は、ZfcRbacを試してみる。
1. ZfcRbacインストール
- https://github.com/ZF-Commons/ZfcRbac
- 公式README.md ままの手順で入った(composer install )。
README に「ZfcRbac Module for Zend Framework 2」とあるけれど、独立したコンポーネントなので、素のPHPや他のフレームワークでもOK。→ZF2と一緒に使うことになります。
2. ZrfRbacサンプルを動かす
間違い1.
Extending and adding roles via instantiation. の ソースコード16 行目 (誤) var_dump($rbac->hasRole('foo')); // true (正)var_dump($rbac->hasRole($foo)); // true
間違い2.
Dynamic Assertions Checking permission using isGranted() with a class implementing Zend\Permissions\Rbac\AssertionInterface. の ソースコード25 行目 (誤) return $this->userId == $article->getUserId(); (正) return $this->userId == $this->article->getUserId();
Rbacでやれること
- Roles
- ロールを登録できるよ
- Permissions
- 認可を設定できるよ
<?php use Zend\Permissions\Rbac\Rbac; use Zend\Permissions\Rbac\Role; $rbac = new Rbac(); $staff = new Role('staff'); $staff->addPermission('read.article'); // ロールがパーミッションを持っているか var_dump($staff->hasPermission('read.article')); // true var_dump($staff->hasPermission('edit.article')); // false $rbac->addRole($staff); // 指定の権限を許可設定しているか $rbac->isGranted('staff', 'read.article'); // true $rbac->isGranted('staff', 'edit.article'); // false
- Dynamic Assertions
- Zend\Permissions\Rbac\AssertionInterfaceを実装したクラスと一緒に isGranted() を使えば、動的な認可判定をさせることもできるよ
動くようにした公式サンプルベースのコード
Dynamic Assertions
/** * * ZF-Commons のライセンスは下記です。 * * Commit Practices & Contributions * All ZF-Commons modules are released under the 3-clause BSD license, unless otherwise stated. Contributions in the form of pull requests, feedback, and ideas are welcome from anyone. * * Once a module reaches its first tagged release, the following rules shall apply: * * All work should be done on feature / hotfix branches (NOT MASTER!) and pushed to your own fork. * When a feature is ready to be merged, submit a pull request. * Those with commit access must not push their commits directly to the canonical repository or merge their own pull requests. * Each pull request should be peer-reviewed by other member in order to keep high code quality and prevent mistakes and ommisions. Once reviewed it is ready to be merged. * * * http://framework.zend.com/manual/2.0/en/modules/zend.permissions.rbac.examples.html * 上記公式サンプルコードを改変したものです。(kumamidori) */ use Zend\Permissions\Rbac\AssertionInterface; use Zend\Permissions\Rbac\Rbac; require_once('vendor/autoload.php'); class AssertUserIdMatches implements AssertionInterface { protected $userId; protected $article; public function __construct($userId) { $this->userId = $userId; } public function setArticle($article) { $this->article = $article; } public function assert(Rbac $rbac) { if (!$this->article) { return false; } // edits his own article // can not edit another users article return $this->userId == $this->article->getUserId(); } } //(略) // User is assigned the foo role with id 5 // News article belongs to userId 5 // Jazz article belongs to userId 6 $rbac = new Rbac(); $user = $mySessionObject->getUser(); $news = $articleService->getArticle(5); $jazz = $articleService->getArticle(6); $rbac->addRole($user->getRole()); $rbac->getRole($user->getRole())->addPermission('edit.article'); $assertion = new AssertUserIdMatches($user->getId()); $assertion->setArticle($news); //Determines if access is granted by checking the role and child roles for permission. // bad!!! true always - bad !!! if ($rbac->isGranted($user->getRole(), 'edit.article')) { // hacks another users article // NG!!! p('1. This is BAD!!! sample. another users article'); } // true for user id 5, because he belongs to write group and user id matches if ($rbac->isGranted($user->getRole(), 'edit.article', $assertion)) { // edits his own article p('2. This is GOOD Sample. edits his own article'); } $assertion->setArticle($jazz); // false for user id 5 if ($rbac->isGranted($user->getRole(), 'edit.article', $assertion)) { // can not edit another users article } else { p('3. This is GOOD Sample. can not edit another users article'); }
TODO(今後の課題)
p.s.
今回、@koriymさんと @futurista999さんから、ライブラリ情報を教えて頂きました。ありがとうございました!