(0)
(0)
(0)
(0)
Total: 0 DAY6: セキュリティーとバリデーション
5日目までのaskeet
5日目でsfFormを使ったログインフォームの実装を行いました。
本家では6日目にバリデーションを行うのですが、すでにsfFormで設定を行っているので、確認しつつ進めていきましょう。
ログインバリデーション
syfmony1.0まではyamlで設定したバリデーションが一般的でした。そのため、アクション部分にバリデーションの処理が含まれていました。
5日目におこなったコーディングのように、symfony1.1以降からはsfFormにてフォームのバリデーションを定義できます。
そのため、アクションの中身はすっきりとし、見通しのよいものになりました。
5日目での実装ではパスワードの認証部分をスルーしていました。
なので、パスワード認証を実装していきましょう。
パスワードフィールドの追加
ask_userテーブルにはパスワードフィールドがありません。
なので、パスワードフィールドを追加することから始めましょう。
やり方は4日目で回答のサンプルデータを投入したときと同じ手順です。
物理的にテーブルにカラムを追加して、スキーマを再作成する方法と、スキーマに変更を加えてSQLを作成し、それを流し込む方法がありますが、ここではスキーマに修正を加える方法を行います。
スキーマの修正
スキーマのUser部分に以下の3項目を追加します。
./config/doctrine/schema.yml
-
User:
-
....
-
email: string(100)
-
sha1_password: string(40)
-
salt: string(32)
モデルの修正
パスワード部分を作成するメソッドを用意します。
./lib/model/doctrine/User.class.php
テストデータの修正
テストデータを修正して用意しておきます。
./data/fixtures/test/003.user.yml
-
anonymous:
-
nickname: anonymous
-
first_name: Anonymous
-
last_name: Coward
-
fabien:
-
nickname: fabpot
-
first_name: Fabien
-
last_name: Potencier
-
password: symfony
-
email: fp@example.com
-
francois:
-
nickname: francoisz
-
first_name: François
-
last_name: Zaninotto
-
password: adventcal
-
email: fz@example.com
コマンドで各変更を反映させる
4日目に用意したdrop.sql, doctrine:build-sql, doctrine:data-load を利用してモデルとデータを再生成します。
* モデルとフォームを再生成する
-
$ ./symfony doctrine:build-model
-
$ ./symfony doctrine:build-form
-
$ ./symfony cc
* テーブルを削除する
-
$ mysql -uyourname -p askeet <./data/sql/drop.sql
-
Enter password:
* モデルからテーブルのCreate文を作成する
-
$ ./symfony doctrine:build-sql
-
>> doctrine generating sql for models
* 作成したSQLを読み込む
-
$ mysql -uyourname -p askeet <./data/sql/schema.sql
-
Enter password:
* テストデータを再投入
-
$ ./symfony doctrine:data-load --dir="data/fixtures/test"
-
>> doctrine loading data fixtures from "Array"
これで変更作業はおわりです。
ログインフォームのバリデーションの修正
新しく追加したフィールドはフォーム画面で使いませんので、unsetしておきます。
./lib/form/doctrine/LoginUserForm.class.php
-
'created_at',
-
'first_name',
-
'last_name',
-
'email',
-
'sha1_password',
-
'salt',
-
);
あとは、スルーさせていたパスワードチェックを実装します。
./lib/form/doctrine/LoginUserForm.class.php
-
$user = User::getValidUserByNickname($values['nickname']);
-
if ($user->count() != 1 || sha1($user[0]->getSalt().$values['password']) != $user[0]->getSha1Password()) {
-
throw new sfValidatorError($validator, '該当するユーザーがいません');
-
return $values;
-
}
これだけで終わりです。簡単ですね。
本家のaskeetでは「アクションからコードを削除します」という作業がありますが、
すでにsfFormに委託されているaskeet1.2ではその作業は必要ありあせん。
アクセス制限
さて、いままでsetLoginAuthで認証を与えていても実際にアクセスできていました。
それは、security.ymlを用意していなかったからです。
というわけで、security.ymlを用意し、質問の追加(question/add)はsubscriberという権限がないと駄目というルールを追加しましょう。
./apps/frontend/modules/question/config/security.yml
-
add:
-
is_security: on
-
credentials: subscriber
-
-
all:
-
is_secure: off
本家でUserクラスに対して signInとsiginOutを実装するコードがでてきます。
ネーミングが綺麗ですね。というわけで5日目に実装したコードを本家に合わせておきましょう。
ネーミングが悪くてすいません。。
./apps/frontend/lib/myUser.class.php
-
class myUser extends sfBasicSecurityUser
-
{
-
public function signIn($user)
-
{
-
$this->setAuthenticated(true);
-
$this->addCredential('subscriber');
-
$this->setAttribute('subscriber_id', $user->getId(), 'subscriber');
-
$this->setAttribute('nickname', $user->getNickname(), 'subscriber');
-
}
-
public function signOut()
-
{
-
$this->setAuthenticated(false);
-
$this->clearCredentials();
-
$this->getAttributeHolder()->removeNamespace('subscriber');
-
}
-
}
./lib/form/doctrine/LoginUserForm.class.php
-
// set auth
-
$sf_user = sfContext::getInstance()->getUser();
-
$sf_user->signIn($user[0]);
./apps/frontend/modules/user/actions/action.class.php
-
public function executeLogout(sfWebRequest $request)
-
{
-
$this->getUser()->signOut();
-
$this->redirect(@homepage);
-
}
さぁこれでかなりすっきりしました。
さらに、Userの属性にアクセスするメソッドも本家にあわせて用意しておきます。
./apps/frontend/lib/myUser.class.php
-
...
-
public function getSubscriberId()
-
{
-
return $this->getAttribute('subscriber_id', '', 'subscriber');
-
}
-
public function getSubscriber()
-
{
-
return User::findById($this->getSubscriberId());
-
}
-
public function getNickname()
-
{
-
return $this->getAttribute('nickname', '', 'subscriber');
-
}
そして、モデルアクセス部分であるUser::findByIdメソッドをモデルに用意しておきます。
./lib/model/doctrine/User.class.php
-
...
-
{
-
return Doctrine::getTable('User')->find($id);
-
}
そして、ユーザープロフィールの変更画面へのリンクをレイアウトで以下のように書く事ができます。
./apps/frontend/templates/layout.php
このあたりはDoctrineを使っているので異なる点と、モデルにDQLを集約している点以外は本家askeetと同じですね。
また明日
今日はかなり少なくて済みました。
というのも、5日目にsfFormを使ってバリデーションを実装しているおかげとも言えます。
ここまでのソースは以下のリポジトリからチェックアウトすることができます。
http://svn.1ms.jp/public/symfony/askeet12/tags/release_day_6
関連するその他の記事
Comments
Leave a Reply