FuelPHPのAuthパッケージを使用して独自の認証ドライバーを作成するための情報


## 記事を書くきっかけ

次の事がしたい。

1. ログイン
1. ログアウト
1. ログイン状態のチェック
1. データベースのリソース単位のアクセス制限
1. moduleレベルのアクセス制限
1. controllerレベルのアクセス制限
1. actionレベルのアクセス制限

_※最初の記事アップ時は、前三つの情報を記載する。_

前提は次の通り。

1. Simpleauthの制約（主にデータベースのテーブル構造とセッションのキー名）にとらわれたくない。
1. Authパッケージを使用するべきだろう。

そもそもAuthパッケージが理解したかった、というのもある。

## ファイルの用意（プログラムファイル）

ドライバー名をFooBarとする。以下の（ディレクトリーと）ファイルを作成する。

1. fuel/app/classes/auth/acl/foobar.php
1. fuel/app/classes/auth/group/foobar.php
1. fuel/app/classes/auth/login/foobar.php

abstractなメソッドを宣言した状態のプログラムは下記のようになる。

```php:fuel/app/classes/auth/acl/foobar.php
<?php

class Auth_Acl_FooBar extends \Auth\Auth_Acl_Driver
{
  /**
   * Check access rights
   *
   * @param  mixed  \Auth\condition to check for access
   * @param  mixed  \Auth\user or group identifier in the form of array(driver_id, id)
   * @return  bool
   */
  public function has_access($condition, Array $entity)
  {
    // TODO: Implement has_access() method.
  }
}
```
```php:fuel/app/classes/auth/group/foobar.php
<?php

class Auth_Group_FooBar extends \Auth\Auth_Group_Driver
{
  /**
   * Check membership of given users
   *
   * @param  mixed  \Auth\condition to check for access
   * @param  array  \Auth\user identifier in the form of array(driver_id, user_id), or null for logged in
   * @return  bool
   */
  public function member($group, $user = null)
  {
    // TODO: Implement member() method.
  }

  /**
   * Fetch the display name of the given group
   *
   * @param  mixed  \Auth\group condition to check
   * @return  string
   */
  public function get_name($group)
  {
    // TODO: Implement get_name() method.
  }
}
```
```php:fuel/app/classes/auth/login/foobar.php
<?php

class Auth_Login_FooBar extends \Auth\Auth_Login_Driver
{
  /**
   * Perform the actual login check
   *
   * @return  bool
   */
  protected function perform_check()
  {
    // TODO: Implement perform_check() method.
  }

  /**
   * Perform the actual login check
   *
   * @return  bool
   */
  public function validate_user()
  {
    // TODO: Implement validate_user() method.
  }

  /**
   * Login method
   *
   * @return  bool  whether login succeeded
   */
  public function login()
  {
    // TODO: Implement login() method.
  }

  /**
   * Logout method
   */
  public function logout()
  {
    // TODO: Implement logout() method.
  }

  /**
   * Get User Identifier of the current logged in user
   * in the form: array(driver_id, user_id)
   *
   * @return  array
   */
  public function get_user_id()
  {
    // TODO: Implement get_user_id() method.
  }

  /**
   * Get User Groups of the current logged in user
   * in the form: array(array(driver_id, group_id), array(driver_id, group_id), etc)
   *
   * @return  array
   */
  public function get_groups()
  {
    // TODO: Implement get_groups() method.
  }

  /**
   * Get emailaddress of the current logged in user
   *
   * @return  string
   */
  public function get_email()
  {
    // TODO: Implement get_email() method.
  }

  /**
   * Get screen name of the current logged in user
   *
   * @return  string
   */
  public function get_screen_name()
  {
    // TODO: Implement get_screen_name() method.
  }
}
```

## ファイルの用意（設定ファイル）

FooBarドライバーを使用するように設定ファイルを作成する。
_※記事を追記して行く過程で本内容は変更になる可能性がある。_

```php:fuel/app/config/auth.php
<?php

return array(
  'driver' => array('FooBar'),
);
```

## Authパッケージの有効化

config.php内で定義している配列からpackagesというキーを探し、authを追加する。
_※記事を追記して行く過程で本内容は変更になる可能性がある。_

```php:fuel/app/config/config.php
  'packages' => array(
    'auth',
  ),
```

ここまでの状態で``` \Auth::instance()->login()```とすると、``` Auth_Login_FooBar#login```が呼び出されるようになっている。

## ログイン関連を実装するための情報

冒頭で記載した、

1. ログイン
1. ログアウト
1. ログイン状態のチェック

を実装するための情報を記載する。実装するのは``` Auth_Login_FooBar```の下記のメソッドになる。

- perform_check
- validate_user
- login
- logout

実際はSimpleauthのようにデータベースとセッションを使用して実装をする事になると思うが、ここでは説明を簡単にするために、データベースを使用しないコードを紹介する。

```php:fuel/app/classes/auth/login/foobar.php
<?php

class Auth_Login_FooBar extends \Auth\Auth_Login_Driver
{
  const SESSION_KEY_LOGGED_IN = 'logged-in';

  /**
   * Perform the actual login check
   *
   * @return  bool
   */
  protected function perform_check()
  {
    // ログインしているかどうかをチェックする目的で本メソッドが使用される。
    // 一般的には、ログイン成功時にセッションに、ログイン済みを意味する値を
    // 格納しているはず。その値が設定されている事を確認する。
    return !empty(\Session::get(self::SESSION_KEY_LOGGED_IN));
  }

  /**
   * Perform the actual login check
   *
   * @return  bool
   */
  public function validate_user($mail_address = '', $password_hash = '')
  {
    // ユーザーを識別するための情報、例えばメールアドレスとパスワードのハッシュを
    // 受け取り、当該情報が有効かどうかを判断する処理を記述する。
    //
    // \Auth\Auth_Login_Driverで本メソッドは、引数無しで定義されているため、
    // 実装時、全ての引数をデフォルト引数とする。
    // デフォルト引数を受け取った場合の処理はSimpleauth等を参考にする事。
    //
    // 尚、本メソッドでログインした事を記録してはいけない。
    // それはlogin()で行う事。

    // ここでは、指定されたメールアドレスが、内部に保持しているメールアドレスと
    // 合致するかどうかだけを判断する。
    // 実際は、メールアドレスとパスワード（のハッシュとか）でデータベースの
    // テーブルを検索して判断する処理等を記述する事になる。
    return in_array($mail_address, array('foo@bar.com', 'bar@baz.com'));
  }

  /**
   * Login method
   *
   * @return  bool  whether login succeeded
   */
  public function login($mail_address = '', $password_hash = '')
  {
    // ログイン処理を記述する。
    // validate_user()がtrueを返却した場合、セッションにログイン済みを
    // 意味する値を格納するのが一般的な実装。
    //
    // デフォルト引数についてはvalidate_user()と同様。
    if(!$this->validate_user($mail_address, $password_hash)){
      return false;
    }

    \Session::set(self::SESSION_KEY_LOGGED_IN, 'ユーザーIDとかとか');

    return true;
  }

  /**
   * Logout method
   */
  public function logout()
  {
    // ログアウト処理を記述する。
    // セッションの、ログイン済みを意味する値を削除するのが一般的な実装。
    \Session::delete(self::SESSION_KEY_LOGGED_IN);
  }

  // その他はここでは省略
}
```

ここまでのテストは、例えば次の通り。

|行いたい事		|呼び出すメソッド					|
|:-------		|:-------						|
|ログイン		|\Auth::instance()->login()	|
|ログアウト		|\Auth::instance()->logout()	|
|ログイン状態確認	|\Auth::instance()->check()	|


```php:fuel/app/tests/auth/foobar.php
<?php

/**
 * @group foobar_auth
 */
class Test_Auth_FooBar extends TestCase
{
  public function test_login()
  {
    $this->assertEquals(false, \Auth::instance()->check());
    $this->assertEquals(true, \Auth::instance()->login('foo@bar.com'));
    $this->assertEquals(true, \Auth::instance()->check());
    \Auth::instance()->logout();
    $this->assertEquals(false, \Auth::instance()->check());
    $this->assertEquals(false, \Auth::instance()->login('fizz@buzz.com'));
    $this->assertEquals(false, \Auth::instance()->check());
    \Auth::instance()->logout();
  }
}
```

実行してみる。

```shell-session:shell
php oil test --group=foobar_auth
```