チョーシドウダ?カラダァ?どうも、高橋です!
今朝、CakePHP2.3.0-beta版がリリースされましたね!
今回のリリースでは、パフォーマンス、セキュリティ、使いやすさを向上させる新機能が追加されました。
また完全に下位互換があるので、2.2系からのマイグレーションは(おそらく)容易でしょう。
というわけで、早速Cake2.3を使ってACLを実現してみたいと思います。
イメージが掴みにくいと思ったのでデモを作成しました。(がんばりました!
ログイン:http://s1.asial.co.jp/~yuya/blog/20121029/users/login
username: adminuser / password: 0000
ACL管理 :http://s1.asial.co.jp/~yuya/blog/20121029/admin/acl
またCakePHPのセットアップについては以下の記事をご覧ください。
http://blog.asial.co.jp/1040
ACLとは、ユーザやグループを元に権限を制御することです。
例えば、ファイルやフォルダのパーミッションですね。
これをWebに置き換えると、↓のような感じです。
「アシアル(グループ)の高橋(ユーザ)はアシアルブログを編集を許可する(権限)」
とっても簡単ですね。
ACLを利用することで、通常の認証機構では困難なフレキシブルな権限管理をいとも簡単に実現出来ます。
っていきなりプラグインに頼るんかい!ってそのリアクション、ありがとうございます。
簡単に扱うというテーマには合致しているので問題ないはず。
ダウンロードはこちら (直リンです)
http://www.alaxos.ch/blaxos/downloads/get/alaxos_acl_2.2.0.zip
このプラグインはCakePHP製CMSで有名なcroogoでも採用されている実績があります。
またファイル名に「2.2.0」とかいう意味深な文字列がありますが、気にしないでおきましょう。
解凍すると「Acl」というフォルダになるので「app/Plugin/Acl」のように配置します。
app/Config/core.php の113行目付近のコメントアウトを解除します。
//Configure::write('Routing.prefixes', array('admin'));
↓
Configure::write('Routing.prefixes', array('admin'));
app/Config/bootstrap.php の145行目付近に以下のコードを追加します。
CakePlugin::load('Acl', array('bootstrap' => true));
これで完了。
まずユーザやグループのテーブルを作成します。
ACLではARO(リクエストオブジェクト)の役割を成します。
CREATE TABLE users (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password CHAR(40) NOT NULL,
group_id INT(11) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE groups (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created DATETIME,
modified DATETIME
);
次に制御するページのテーブルです。
こちらはACO(コントロールオブジェクト)となります。
CREATE TABLE posts (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT(11) NOT NULL,
title VARCHAR(255) NOT NULL,
body TEXT,
created DATETIME,
modified DATETIME
);
CREATE TABLE widgets (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
part_no VARCHAR(12),
quantity INT(11)
);
cd /Users/yuya/html/cake/app
./Config/cake bake all
1
./Config/cake bake all
2
./Config/cake bake all
3
./Config/cake bake all
4
上手に焼けましたーーッ!
あ、まだコンソールは閉じないでくださいね。
コンソールで一行実行するだけなので簡単です。
./Console/cake schema create DbAcl
y
y
これで acos、aros、aros_acos という
ACLに関連する3つのテーブルが初期化されました。
追加はDBから直接行ったりはせず、Cakeのアプリケーションで行います。
そこで認証機構の準備とログイン処理を作成します。
app/Controller/UsersController.php
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController {
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Your username or password was incorrect.');
}
}
}
public function logout() {
//Leave empty for now.
}
~~~~
app/View/Users/login.ctp
<?php
echo $this->Form->create('User', array('action' => 'login'));
echo $this->Form->inputs(array(
'legend' => __('Login'),
'username',
'password'
));
echo $this->Form->end('Login');
app/Model/User.php
<?php
App::uses('AppModel', 'Model');
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public function beforeSave($options = array()) {
$this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
return true;
}
~~~
app/Controller/AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Acl',
'Auth' => array(
'authorize' => array(
'Actions' => array('actionPath' => 'controllers')
)
),
'Session'
);
public $helpers = array('Html', 'Form', 'Session');
public function beforeFilter() {
//Configure AuthComponent
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
}
}
app/Model/User.php
<?php
App::uses('AppModel', 'Model');
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public $belongsTo = array('Group');
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
if (!$this->id & & empty($this->data)) {
return null;
}
if (isset($this->data['User']['group_id'])) {
$groupId = $this->data['User']['group_id'];
} else {
$groupId = $this->field('group_id');
}
if (!$groupId) {
return null;
} else {
return array('Group' => array('id' => $groupId));
}
}
~~~~
app/Model/Group.php
<?php
App::uses('AppModel', 'Model');
class Group extends AppModel {
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
return null;
}
~~~
これからグループとユーザを追加しますが、「http://~~~~/users」 にアクセスしても
ログインページにリダイレクトされてしまうでしょう。
一時的に認証を解除するためのコードを挿入します。
app/Controller/UsersController.php
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController {
// すべてのアクションを許可する
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow();
}
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Your username or password was incorrect.');
}
}
}
public function logout() {
//Leave empty for now.
}
~~~
app/Controller/GroupsController.php
<?php
App::uses('AppController', 'Controller');
class GroupsController extends AppController {
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow();
}
~~~
ここで何回やっても許可したはずなのにログインページに飛ぶ!!ってハマったんですが
前は $this->Auth->allow(‘*’); ですべてのアクションを許可だったはずなんですが
今は $this->Auth->allow(); になっているようです。気をつけてください。
余談ですが、他の指定法としては
$this->Auth->allow(array('edit', 'add'));
$this->Auth->allow('edit', 'add');
があります。
さて、無事にページが表示できたらデータを登録していきます。
絶対にグループから登録してください(重要
今回は形式的に
グループ:administrators、managers、users
ユーザ :adminuser、manageuser、useruser
というパターンを作成しましょう。
グループやユーザの追加は必ずCakeのアプリケーションから追加してください。
mysql> SELECT * FROM aros;
+----+-----------+-------+-------------+-------+------+------+
| id | parent_id | model | foreign_key | alias | lft | rght |
+----+-----------+-------+-------------+-------+------+------+
| 1 | NULL | Group | 1 | NULL | 1 | 4 |
| 2 | NULL | Group | 2 | NULL | 5 | 8 |
| 3 | NULL | Group | 3 | NULL | 9 | 12 |
| 4 | 1 | User | 1 | NULL | 2 | 3 |
| 5 | 2 | User | 2 | NULL | 6 | 7 |
| 6 |