symfony askeet Day 6続き

ユーザー認証

まずはバリデータの設定をlogin.ymlに書く。

場所はaskeet/apps/frontend/modules/user/validate/login.yml
設定は完了したけど、テストデータにもデータモデルにもパスワードが存在しないので実装します。
まずはschema.ymlに実装。

  ask_user:
    _attributes: { phpName: User, idMethod: native }
    id:          { type: integer, required: true, primaryKey: true, autoIncrement: true }
    nickname:    { type: varchar(50), required: true, index: true }
    first_name:  varchar(100)
    last_name:   varchar(100)
    email:       varchar(100)
    sha1_password: varchar(40)
    salt:        varchar(32)
    created_at:  ~

書き終わったらsymfony propel-build-modelをして
symfony propel-build-sqlSQL文を発行して
symfony propel-insert-sqlでMySQLSQL文を流します。

パスワードの保存を行う処理をaskeet/lib/model/User.phpに実装。
public function setPassword($password)
{
  $salt = md5(rand(100000, 999999).$this->getNickname().$this->getEmail());
  $this->setSalt($salt);
  $this->setSha1Password(sha1($salt.$password));
}

$saltにはランダムな数とnicknameとemailをくっつけたものをMD5で暗号化したもの
それをパスワードとくっつけてSHA1で暗号化したものがsha1_passwordらしい。
sha1って何だろうと思ったので検索する。
調べてみるとMD4を元にした暗号化技術らしいのですが
どうも攻撃に成功してしまったらしい。つまり、復号可能ってことらしい。
だから、$saltを作ってパスワードが分かりづらくしていると。
参考:パスワードの保存に SMD5 (Salted MD5) や SSHA1を使う (MD5 への辞書攻撃とか) - まちゅダイアリー(2007-10-23)
参考:デファクトスタンダード暗号技術の大移行(1):すべてはここから始まった〜SHA-1の脆弱化 (1/2) - @IT

次にテストデータにパスワードとメールアドレスを追加する。

askeet/data/fixtures/test_data.php

User:
  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:   adventical
    email:      fz@example.com

  shota:
    nickname:   crazyup
    first_name: Shota
    last_name:  Enomoto
    password:   shota
    email:      crazyup@example.com

こんな感じで追加したら

php batch/load_data.php

で、反映。

バリデータの実装。

askeet/apps/frontend/lib/にmyLoginValidator.class.phpを作成。
そして、実装する。
実装が終わったらアクションにあったバリデーションコードは不要なので除去する。

アクセスを制限する

askeetは新規質問、関心を表す、コメントを評価するときなどにログインを求められるが、
それ以外はログインしなくても見られます。
ということでQuestionのaddアクションのアクセスを制限するために
askeet/apps/frontend/modules/question/config/security.ymlを作成する。

add:
  is_secure:    on
  credentials:  subscriber

all:
  is_secure:    off

addアクションにアクセスするには証明が必要だよと。
証明はsubscriberだよと。
それ以外は制限は掛けてない。こういう内容ですね。
ふむふむ。

内部構造の整理(リファクタリング)

ログインとログアウトをmyUser.class.phpにまとめる。

  public function signIn($user)
  {
    $this->setAttribute('subscriber_id',$user->getId(),'subscriber');
    $this->setAuthenticated(true);
    
    $this->addCredential('subscriber');
    $this->setAttribute('nickname',$user->getNickname(),'subscriber');
  }

  public function signOut()
  {
    $this->getAttributeHolder()->removeNamespace('subscriber');
    
    $this->setAuthenticated(false);
    $this->clearCredentials();
  }

ほげーっとまとめたらmyLoginValidator.class.phpの↓のように修正。

        // password is OK? (パスワードは大丈夫?)
        if (sha1($user->getSalt().$password) == $user->getSha1Password())
          {
            $this->getContext()->getUser()->signIn($user);

            return true;
          }

askeet/apps/frontend/modules/user/actions/actions.class.php
書かれているログアウト部分も修正。

  public function executeLogout()
  {
    $this->getUser()->signOut();
    
    $this->redirect('@homepage');
  }

ほげっと終わらせたら、myUser.class.php
getSubscriverIdとgetSubscriberとgetNicknameのメソッドを書く。

  public function getSubscriberId()
  {
    return $this->getAttribute('subscriber_id','','subscriber');
  }

  public function getSubscriber()
  {
    return UserPeer::retrieveByPk($this->getSubscriberId());
  }

  public function getNickname()
  {
    return $this->getAttribute('nickname','','subscriber');
  }

getNicknameはlayout.phpで使えるみたいなので使う。

  <div id="header">
    <ul>
<?php if ($sf_user->isAuthenticated()): ?>
  <li><?php echo link_to('sign out', 'user/logout') ?></li>
  <li><?php echo link_to($sf_user->getNickname().' profile', 'user/profile') ?></li>
<?php else: ?>
  <li><?php echo link_to('sign in/register', 'user/login') ?></li>
<?php endif ?>
      <li><?php echo link_to('about', '@homepage') ?></li>
    </ul>
    <h1><?php echo link_to(image_tag('askeet_logo.gif', 'alt=askeet'), '@homepage') ?></h1>
  </div>