ACLアドオン(アクセス制御) | Addons

ACLアドオンを導入すると、モジュールやコントローラ単位でのアクセス制御が可能になります。アクセス制御の設定は addon/acl/Config.php で行います。

インストール直後の設定を見てみましょう。
public function configure()
{
  $index = $this->module("index");
  $index->allow();
}
初期状態では indexモジュール が public となっています。allow()メソッド を引数無しでコールすると、そのモジュール(またはコントローラ)は public となります。allow()メソッド にはアクセスを許可するロールを指定します。
例えば下記は manager ロールが与えられたユーザに indexモジュール へのアクセス権限を与えます。
public function configure()
{
  $index = $this->module("index");
  $index->allow("manager");
}
この状態でindexモジュールへリクエストすると 403 FORBIDDEN となります。
ロールの指定にはORやAND、括弧を用いることも可能です。
$index->allow("(foo|bar)&baz");
アクセス制限はコントローラごとに行うことも可能です。よくある例として「userモジュールはアクセス制御(ログインしているユーザのみ)を行うが、login と register コントローラだけは public とする」の設定例を下記に示します。
$user = $this->module("user");
$user->allow("user");
$user->controller("login")->allow();
$user->controller("register")->allow();
controllers()メソッド を使用して複数のコントローラの一括設定も可能です。
$user = $this->module("user");
$user->allow("user");
$user->controllers("login", "register")->allow();
ロールを与えるには ACLユーザオブジェクト の addRole()メソッド をコールします。コントローラ(アクション)内からは、$this->aclUser でアクセスできます。
public function myAction()
{
  $this->aclUser->addRole("user");
}
また login()メソッド を使用することもできます。第1引数はログイン後のリダイレクト先を指定します。通常はマイページのような場所に遷移させるでしょう。
public function loginAction()
{
  $this->aclUser->login("n: user, c: main, a: top", "user");
}
ACLユーザオブジェクトは内部でセッションを使用していますので、リクエストをまたぐ継続されたデータを扱うことができます。
$this->aclUser->id = $aUser->id;
$aUser = new User($this->aclUser->id);

スコープ

サービスには一般ユーザと管理者ユーザが存在することがあります。そのようなサービスでは userモジュール と manageモジュール を作成することがあります。
config/Map.php

class Config_Map extends Sabel_Map_Configurator
{
  public function configure()
  {
    $this->route("user")
           ->uri("user/:controller/:action")
           ->module("user")
           ->defaults(array(
             ":controller" => "index",
             ":action"     => "index"
           ));
    
    $this->route("manage")
           ->uri("manage/:controller/:action")
           ->module("manage")
           ->defaults(array(
             ":controller" => "index",
             ":action"     => "index"
           ));
    
    ...
  }
}
この時、ACLの設定は下記のようになります。
addon/acl/Config.php

class Acl_Config implements Sabel_Config
{
  ...
  
  public function configure()
  {
    ...
    
    $user = $this->module("user");
    $user->allow("user");
    
    $manage = $this->module("manage");
    $manage->allow("manager");
  }
  
  ...
}
複数のモジュールが存在するとき、データのキーが重複する可能性が高くなります。
例えば、管理者ログイン後、管理者IDをACLユーザオブジェクト(セッション)に格納します。
$this->aclUser->addRole("manager");
$this->aclUser->id = $aManager->id;
管理者はサービスを覗くために、別のタブなどで一般ユーザとしてログインするかもしれません。この時、下記のように idキー にユーザIDを格納するようにコーディングしていると管理者IDが上書きされてしまい、管理者はログアウトされた状態となってしまいます。
$this->aclUser->addRole("user");
$this->aclUser->id = $aUser->id;
この問題を回避するためにスコープという概念(機能)があります。スコープを指定するには下記のように設定します。
$user = $this->module("user");
$user->scope("user");
$user->allow("user");

$manage = $this->module("manage");
$manage->scope("manager");
$manage->allow("manager");
上記の例では userモジュール へのリクエスト時はスコープを "user" に、manageモジュール へのリクエスト時はスコープを "manager" に設定しています。スコープを設定することで、同じ idキー でも他の場所に格納されることとなります。
class User_Controllers_MyController extends Sabel_Controller_Page
{
  public function myAction()
  {
    ...
    
    $this->aclUser->id = $aUser->id;  // data[user][id] = $aUser->id
  }
}
class Manager_Controllers_MyController extends Sabel_Controller_Page
{
  public function myAction()
  {
    ...
    
    $this->aclUser->id = $aManager->id;  // data[manager][id] = $aManager->id
  }
}
set()メソッド の第3引数や get()メソッド の第2引数で指定したスコープのデータにアクセスすることも可能です。
public function myAction()
{
  // data[user][key] = "value"
  $this->aclUser->set("key", "value", "user");
  
  // data[manager][key]
  $this->aclUser->get("key", "manager");
}
ACL設定でスコープを指定していない場合は Acl_User::DEFAULT_SCOPE に格納されます。下記は明示的にそのデフォルトスコープにset, getするコーディング例です。
public function myAction()
{
  $this->aclUser->set("key", "value", Acl_User::DEFAULT_SCOPE);
  $this->aclUser->get("key", Acl_User::DEFAULT_SCOPE);
}

ログインページへのリダイレクトとログイン後のリダイレクト先

アクセス許可がされていない(ロールが与えられてない)時は通常 403 FORBIDDEN となるだけですが、ログインページへリダイレクトすることもACL設定で可能です。例えば userモジュール は "user" ロールが必要で、ログインページの内部URIが "n: default, c: login, a: form" の時、下記のような設定となります。未ログイン状態で userモジュール にアクセスするとログインページへリダイレクトされます。
$user = $this->module("user");
$user->allow("user");
$user->authUri("n: default, c: login, a: form");
ログインアクションは通常と同様、下記のようになります。
ただし、authUriメソッドによってログインページが設定されている場合はこのログイン後リダイレクト先(第1引数)ではなく、ログイン前にアクセスしたページへ遷移されます。
public function loginAction()
{
  $this->aclUser->login("n: user, c: main, a: top", "user");
}
なお、ログイン前のアクセスがPOSTによるリクエストであったり、ログインページへリダイレクト後、他のページに1度でも移動した場合はログイン前にアクセスしたページへ戻ることはありません。

ログウアウト処理

ログアウトをするにはdeAuthenticate()メソッドを使用します。
public function logoutAction()
{
  $this->aclUser->deAuthenticate();
}