1. 前提
- FuelPHP 1.7
- SimpleAuthを利用
- ユーザ作成をSimpleAuth にまかせている。
2. どう実装しよう?
- authパッケージ内を直接編集
- authパッケージを拡張してカスタムクラスを利用
2.の方が後々楽そう。
3. そもそもなぜEmailの必須を外すのか?
理由はいろいろとあると思いますが、社内システムを作るにあたり、登録予定のユーザの中に社内メールの割当がないとか、特定ユーザ郡はメールアドレスをメーリスにして登録メールアドレスが重複してしまうとかでしょうか?
4. 用意するファイル。
正しいかどうかわからないのが悔やまれるところですが、下記に配置する。
packages¥customauth
ディレクトリ構造はこんな感じ
必要なファイルをauthパッケージからコピー
packages¥auth¥classes¥login¥simpleauth.php
packages¥auth¥bootstrap.php
コピー後のディレクトリ構造
- packages
┗ customauth
┗ classes
┗ auth
┗ login
simpleauth.php
bootstrap.php
5. 中身を見てみよう。
コピーしたファイルを舐めていきます。
ユーザ作成メソッド
packages¥customauth¥classes¥login¥simpleauth.php
内の・・・
この辺があやしい・・・というかまんまだ!
編集前
/**
* Create new user
*
* @param string
* @param string
* @param string must contain valid email address
* @param int group id
* @param Array
* @return bool
*/
public function create_user($username, $password, $email, $group = 1, Array $profile_fields = array())
{
$password = trim($password);
$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);
if (empty($username) or empty($password) or empty($email))
{
throw new \SimpleUserUpdateException('Username, password or email address is not given, or email address is invalid', 1);
}
$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->or_where('email', '=', $email)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if ($same_users->count() > 0)
{
if (in_array(strtolower($email), array_map('strtolower', $same_users->current())))
{
throw new \SimpleUserUpdateException('Email address already exists', 2);
}
else
{
throw new \SimpleUserUpdateException('Username already exists', 3);
}
}
$user = array(
'username' => (string) $username,
'password' => $this->hash_password((string) $password),
'email' => $email,
'group' => (int) $group,
'profile_fields' => serialize($profile_fields),
'last_login' => 0,
'login_hash' => '',
'created_at' => \Date::forge()->get_timestamp(),
);
$result = \DB::insert(\Config::get('simpleauth.table_name'))
->set($user)
->execute(\Config::get('simpleauth.db_connection'));
return ($result[1] > 0) ? $result[0] : false;
}
上記ソースを下記に改定。
/**
* Create new user
*
* @param string
* @param string
* @param string
* @param int group id
* @param Array
* @return bool
*/
public function create_user($username, $password, $email = null, $group = 1, Array $profile_fields = array())
{
$password = trim($password);
if(!empty($email))
{
$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);
if(empty($email))
{
throw new \SimpleUserUpdateException('emailの形式が不正です。', 1);
}
}
if (empty($username) or empty($password))
{
throw new \SimpleUserUpdateException('Username, password is not given, or email address is invalid', 1);
}
$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if ($same_users->count() > 0)
{
throw new \SimpleUserUpdateException('Username already exists', 3);
}
$user = array(
'username' => (string) $username,
'password' => $this->hash_password((string) $password),
'email' => $email,
'group' => (int) $group,
'profile_fields' => serialize($profile_fields),
'last_login' => 0,
'login_hash' => '',
'created_at' => \Date::forge()->get_timestamp(),
);
$result = \DB::insert(\Config::get('simpleauth.table_name'))
->set($user)
->execute(\Config::get('simpleauth.db_connection'));
return ($result[1] > 0) ? $result[0] : false;
}
こんな感じ。
ログインチェック
ログイン時にユーザの存在チェックをするメソッドを発見したのでこれもemailのチェックを外しておく。
/**
* Check the user exists
*
* @return bool
*/
public function validate_user($username_or_email = '', $password = '')
{
$username_or_email = trim($username_or_email) ?: trim(\Input::post(\Config::get('simpleauth.username_post_key', 'username')));
$password = trim($password) ?: trim(\Input::post(\Config::get('simpleauth.password_post_key', 'password')));
if (empty($username_or_email) or empty($password))
{
return false;
}
$password = $this->hash_password($password);
$user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where_open()
->where('username', '=', $username_or_email)
->or_where('email', '=', $username_or_email)
->where_close()
->where('password', '=', $password)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'))->current();
return $user ?: false;
}
email周りをごっそり削除
/**
* Check the user exists
*
* @return bool
*/
public function validate_user($username = '', $password = '')
{
$username = trim($username) ?: trim(\Input::post(\Config::get('simpleauth.username_post_key', 'username')));
$password = trim($password) ?: trim(\Input::post(\Config::get('simpleauth.password_post_key', 'password')));
if (empty($username) or empty($password))
{
return false;
}
$password = $this->hash_password($password);
$user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where_open()
->where('username', '=', $username)
->where_close()
->where('password', '=', $password)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'))->current();
return $user ?: false;
}
ログインメソッド
重要じゃないけど変数名に違和感ができてしまうので修正しておく。
/**
* Login user
*
* @param string
* @param string
* @return bool
*/
public function login($username_or_email = '', $password = '')
{
if ( ! ($this->user = $this->validate_user($username_or_email, $password)))
{
$this->user = \Config::get('simpleauth.guest_login', true) ? static::$guest_login : false;
\Session::delete('username');
\Session::delete('login_hash');
return false;
}
// register so Auth::logout() can find us
Auth::_register_verified($this);
\Session::set('username', $this->user['username']);
\Session::set('login_hash', $this->create_login_hash());
\Session::instance()->rotate();
return true;
}
/**
* Login user
*
* @param string
* @param string
* @return bool
*/
public function login($username = '', $password = '')
{
if ( ! ($this->user = $this->validate_user($username, $password)))
{
$this->user = \Config::get('simpleauth.guest_login', true) ? static::$guest_login : false;
\Session::delete('username');
\Session::delete('login_hash');
return false;
}
// register so Auth::logout() can find us
Auth::_register_verified($this);
\Session::set('username', $this->user['username']);
\Session::set('login_hash', $this->create_login_hash());
\Session::instance()->rotate();
return true;
}
ユーザ情報更新メソッド
こいつもemailの重複チェックとかしてるので修正が必要。
/**
* Update a user's properties
* Note: Username cannot be updated, to update password the old password must be passed as old_password
*
* @param Array properties to be updated including profile fields
* @param string
* @return bool
*/
public function update_user($values, $username = null)
{
$username = $username ?: $this->user['username'];
$current_values = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if (empty($current_values))
{
throw new \SimpleUserUpdateException('Username not found', 4);
}
$update = array();
if (array_key_exists('username', $values))
{
throw new \SimpleUserUpdateException('Username cannot be changed.', 5);
}
if (array_key_exists('password', $values))
{
if (empty($values['old_password'])
or $current_values->get('password') != $this->hash_password(trim($values['old_password'])))
{
throw new \SimpleUserWrongPassword('Old password is invalid');
}
$password = trim(strval($values['password']));
if ($password === '')
{
throw new \SimpleUserUpdateException('Password can\'t be empty.', 6);
}
$update['password'] = $this->hash_password($password);
unset($values['password']);
}
if (array_key_exists('old_password', $values))
{
unset($values['old_password']);
}
if (array_key_exists('email', $values))
{
$email = filter_var(trim($values['email']), FILTER_VALIDATE_EMAIL);
if ( ! $email)
{
throw new \SimpleUserUpdateException('Email address is not valid', 7);
}
$matches = \DB::select()
->where('email', '=', $email)
->where('id', '!=', $current_values[0]['id'])
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if (count($matches))
{
throw new \SimpleUserUpdateException('Email address is already in use', 11);
}
$update['email'] = $email;
unset($values['email']);
}
if (array_key_exists('group', $values))
{
if (is_numeric($values['group']))
{
$update['group'] = (int) $values['group'];
}
unset($values['group']);
}
if ( ! empty($values))
{
$profile_fields = @unserialize($current_values->get('profile_fields')) ?: array();
foreach ($values as $key => $val)
{
if ($val === null)
{
unset($profile_fields[$key]);
}
else
{
$profile_fields[$key] = $val;
}
}
$update['profile_fields'] = serialize($profile_fields);
}
$update['updated_at'] = \Date::forge()->get_timestamp();
$affected_rows = \DB::update(\Config::get('simpleauth.table_name'))
->set($update)
->where('username', '=', $username)
->execute(\Config::get('simpleauth.db_connection'));
// Refresh user
if ($this->user['username'] == $username)
{
$this->user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'))->current();
}
return $affected_rows > 0;
}
/**
* Update a user's properties
* Note: Username cannot be updated, to update password the old password must be passed as old_password
*
* @param Array properties to be updated including profile fields
* @param string
* @return bool
*/
public function update_user($values, $username = null)
{
$username = $username ?: $this->user['username'];
$current_values = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if (empty($current_values))
{
throw new \SimpleUserUpdateException('Username not found', 4);
}
$update = array();
if (array_key_exists('username', $values))
{
throw new \SimpleUserUpdateException('Username cannot be changed.', 5);
}
if (array_key_exists('password', $values))
{
if (empty($values['old_password'])
or $current_values->get('password') != $this->hash_password(trim($values['old_password'])))
{
throw new \SimpleUserWrongPassword('Old password is invalid');
}
$password = trim(strval($values['password']));
if ($password === '')
{
throw new \SimpleUserUpdateException('Password can\'t be empty.', 6);
}
$update['password'] = $this->hash_password($password);
unset($values['password']);
}
if (array_key_exists('old_password', $values))
{
unset($values['old_password']);
}
if (array_key_exists('email', $values))
{
$email = filter_var(trim($values['email']), FILTER_VALIDATE_EMAIL);
if ( ! $email)
{
throw new \SimpleUserUpdateException('Email address is not valid', 7);
}
$update['email'] = $email;
unset($values['email']);
}
if (array_key_exists('group', $values))
{
if (is_numeric($values['group']))
{
$update['group'] = (int) $values['group'];
}
unset($values['group']);
}
if ( ! empty($values))
{
$profile_fields = @unserialize($current_values->get('profile_fields')) ?: array();
foreach ($values as $key => $val)
{
if ($val === null)
{
unset($profile_fields[$key]);
}
else
{
$profile_fields[$key] = $val;
}
}
$update['profile_fields'] = serialize($profile_fields);
}
$update['updated_at'] = \Date::forge()->get_timestamp();
$affected_rows = \DB::update(\Config::get('simpleauth.table_name'))
->set($update)
->where('username', '=', $username)
->execute(\Config::get('simpleauth.db_connection'));
// Refresh user
if ($this->user['username'] == $username)
{
$this->user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'))->current();
}
return $affected_rows > 0;
}
namespaceの決定
namespace Auth;
namespace CustomAuth;
class の継承元変更
class Auth_Login_Simpleauth extends \Auth_Login_Driver
class Auth_Login_Simpleauth extends \Auth\Auth_Login_Simpleauth
bootstrap.phpの修正
下記ファイルを修正する。
packages¥auth¥bootstrap.php
<?php
/**
* Fuel
*
* Fuel is a fast, lightweight, community driven PHP5 framework.
*
* @package Fuel
* @version 1.7
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2015 Fuel Development Team
* @link http://fuelphp.com
*/
\Autoloader::add_core_namespace('Auth');
\Autoloader::add_classes(array(
'Auth\\Auth' => __DIR__.'/classes/auth.php',
'Auth\\AuthException' => __DIR__.'/classes/auth.php',
'Auth\\Auth_Driver' => __DIR__.'/classes/auth/driver.php',
'Auth\\Auth_Opauth' => __DIR__.'/classes/auth/opauth.php',
'Auth\\Auth_Acl_Driver' => __DIR__.'/classes/auth/acl/driver.php',
'Auth\\Auth_Acl_Simpleacl' => __DIR__.'/classes/auth/acl/simpleacl.php',
'Auth\\Auth_Acl_Ormacl' => __DIR__.'/classes/auth/acl/ormacl.php',
'Auth\\Auth_Group_Driver' => __DIR__.'/classes/auth/group/driver.php',
'Auth\\Auth_Group_Simplegroup' => __DIR__.'/classes/auth/group/simplegroup.php',
'Auth\\Auth_Group_Ormgroup' => __DIR__.'/classes/auth/group/ormgroup.php',
'Auth\\Auth_Login_Driver' => __DIR__.'/classes/auth/login/driver.php',
'Auth\\Auth_Login_Simpleauth' => __DIR__.'/classes/auth/login/simpleauth.php',
'Auth\\Auth_Login_Ormauth' => __DIR__.'/classes/auth/login/ormauth.php',
'Auth\\SimpleUserUpdateException' => __DIR__.'/classes/auth/exceptions.php',
'Auth\\SimpleUserWrongPassword' => __DIR__.'/classes/auth/exceptions.php',
'Auth\\OpauthException' => __DIR__.'/classes/auth/exceptions.php',
'Auth\\Model\\Auth_User' => __DIR__.'/classes/model/auth/user.php',
'Auth\\Model\\Auth_Userpermission' => __DIR__.'/classes/model/auth/userpermission.php',
'Auth\\Model\\Auth_Metadata' => __DIR__.'/classes/model/auth/metadata.php',
'Auth\\Model\\Auth_Group' => __DIR__.'/classes/model/auth/group.php',
'Auth\\Model\\Auth_Grouppermission' => __DIR__.'/classes/model/auth/grouppermission.php',
'Auth\\Model\\Auth_Role' => __DIR__.'/classes/model/auth/role.php',
'Auth\\Model\\Auth_Rolepermission' => __DIR__.'/classes/model/auth/rolepermission.php',
'Auth\\Model\\Auth_Permission' => __DIR__.'/classes/model/auth/permission.php',
'Auth\\Model\\Auth_Provider' => __DIR__.'/classes/model/auth/provider.php',
));
<?php
\Autoloader::add_core_namespace('CustomAuth');
\Autoloader::add_classes(array(
'CustomAuth\\Auth_Login_Simpleauth' => __DIR__.'/classes/auth/login/simpleauth.php',
));
結構いろいろ直したなぁ・・・
上記で動くようになると思います。
注意
上記修正は一貫してソース周りだけです。
migrationファイルでDB作成を行っている場合、emailにUK,NN制約が付いているので外してあげましょう。
GitHub Link
https://github.com/unagiya/FuelPHP_1_7_CustomAuth
https://github.com/bikun-bikun/FuelPHP_1_7_CustomAuth
2015-12-14追記
パッケージに追加したので、configから該当パッケージを読めるようにしないとダメでした。
config.phpから作成したパッケージを読み込めるように修正
修正するファイル
fuel\app\config\config.php
'packages' => array(
'auth',
'parser',
'orm',
'less',
),
'packages' => array(
'auth',
'parser',
'orm',
'less',
'customauth',
),