株式会社クリアコード > ククログ

ククログ


ActiveLdap 1.2.2 - Rails 2.3.8対応

RubyらしいAPIでLDAPのエントリを操作できるライブラリActiveLdapの新しいバージョンがリリースされました。以下のようにgemでアップデートできます。

% sudo gem install activeldap

ActiveLdapについてはこのあたりを見てください。

今回のリリースではRuby on Railsの最新安定版2.3.8に対応しました。ActiveLdapは国際化対応のためにRuby-GetText-Packageを使っています。そのため、ActiveLdapをRailsで使う場合にlocale_railsと一緒に使っている場合も多いでしょう。しかし、locale_railsの最新版はRails 2.3.8に対応していないので、locale_railsを利用している場合はアップデートするかどうかよく検討してください。(locale_railsのリポジトリ上では2.3.8に対応しているので、locale_railsのリリース版ではなくて未リリースのものを利用するのも対応策の1つです。)

LDAPといえば、日本Ruby会議2010では「Rubyで扱うLDAPのススメ」という企画があります。[ANN]RubyKaigi2010 企画 "Ruby で扱う LDAP のススメ" にご協力頂ける方を募集しています - tashenの日記ということなので、ぜひ、ご協力をお願いします。

タグ: Ruby

この記事の続き

2010-07-05

Rails 3.0 beta4でDeviseを使ってOpenID認証

とあるRails 3を使っているたいやき用のCMSでDeviseを使ってOpenID認証をするようにしたので、そのやり方を紹介します。RubyはRuby 1.9.2 RC2も出ていますが、今回はRuby 1.9.1を使います。

Deviseとは

DeviseRackベースの認証システムです。バックエンドにWardenを利用しているため、Basic認証やOpenID、OAuthなど認証方法を切り替えることができます。

ただ、以下の説明を読んでみてもらってもわかる通り、動き出すまでにそこそこの作業が必要になります。機能は豊富なので、動き出したらカスタマイズしてアプリケーションの要求に合わせていくことができるでしょう。日本語での情報もあまりありませんが、探せばいくつかはあるので、試してみてはいかがでしょうか。

とはいえ、今回はDeviseのデフォルトの認証方法ではなく、OpenIDでのみ認証することにします。また、未登録のユーザがログインしようとしたときは自動的に新規ユーザを作成することにします。このようにも使えるという例ということで読むとよいかもしれません。

インストール

まず、Rails 3.0 beta4をインストールします。

% sudo gem1.9.1 install rails --pre

サンプル用のアプリケーションを作ります。

% ruby1.9.1 rails new taiyaki
% cd taiyaki

次にDeviseをインストールします。

% sudo gem1.9.1 install devise --version=1.1.rc2

インストールしたDeviseを利用するため、以下のようにGemfileに追記します。

Gemfile:

1
gem 'devise', "1.1.rc2"

アプリケーションにDeviseが動作するために必要なファイルをインストールします。config/initializers/devise.rbなど主に設定ファイルです。

% ruby1.9.1 script/rails generate devise:install

いくつかは手動で設定する必要があります。それぞれ以下の通りです。

Deviseはパスワードの再設定をする機能もあり、そのときはユーザにメールを送信します。そのような機能を使うときはActionMailerのURL生成オプションを設定する必要があります。例えば、開発時のホスト情報を設定する場合は以下のようになります。

config/environments/development.rb:

1
config.action_mailer.default_url_options = {:host => 'localhost:3000'}

Deviseはリダイレクト先のURLを生成するときなどにデフォルトではroot_pathを使うので、rootパスへのマッピングを追加します。以下の例ではwelcome#indexを指定しているので、後でWelcomeControllerを作ります。

config/routes.rb:

1
root :to => "welcome#index"

Deviseはnoticeとalertのflashを設定するので、レイアウトに追加しておくとよいでしょう。例えば、以下のようにyieldの前に追加します。

app/views/layouts/application.html.erb:

<%# ... %>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

<%= yield %>
<%# ... %>

作成

これでインストールは完了したので、コントローラーやモデルを作成します。

まず、ユーザー用のモデルを作成します。

% ruby1.9.1 script/rails generate devise User

このとき生成されるスキーマは、以下のようにデータベース上にパスワードのダイジェストなどの情報を持ち、それを利用して認証することになります。

db/migrate/XXXX_devise_create_users.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class DeviseCreateUsers < ActiveRecord::Migration
  def self.up
    create_table(:users) do |t|
      t.database_authenticatable :null => false
      t.recoverable
      t.rememberable
      t.trackable

      # t.confirmable
      # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both

      # t.token_authenticatable

      t.timestamps
    end

    add_index :users, :email,                :unique => true
    add_index :users, :reset_password_token, :unique => true
    # add_index :users, :confirmation_token,   :unique => true
    # add_index :users, :unlock_token,         :unique => true
  end

  def self.down
    drop_table :users
  end
end

しかし、今回は自分では認証情報を持たずにOpenIDで認証するので、データベース上に認証情報を持たないようにします。代わりにOpenID用のカラムを追加します。

db/migrate/XXXX_devise_create_users.rb(変更後):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class DeviseCreateUsers < ActiveRecord::Migration
  def self.up
    create_table(:users) do |t|
      t.string :email
      t.string :nickname
      t.string :identity_url
      t.string :fullname
      t.string :birth_date
      t.integer :gender
      t.string :postcode
      t.string :country
      t.string :language
      t.string :timezone

      t.rememberable
      t.trackable

      t.confirmable
      t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
      t.token_authenticatable

      t.timestamps
    end

    add_index :users, :identity_url,         :unique => true
    add_index :users, :email,                :unique => true
    add_index :users, :confirmation_token,   :unique => true
    add_index :users, :unlock_token,         :unique => true
  end

  def self.down
    drop_table :users
  end
end

変更したらスキーマを反映させます。

% rake1.9.1 db:migrate

モデルのコードにもデータベースで認証するためのコードが入っています。今回は必要のないユーザ登録用の:registerableオプションやパスワードの入力チェックなどをする:validatableオプションなどは外します。password_required?メソッドをオーバーライドしているのは、OpenIDで認証するためパスワードが必要がないからです。

app/models/user.rb:

1
2
3
4
5
6
7
8
9
10
11
class User < ActiveRecord::Base
  ...
  # devise :database_authenticatable, :registerable,
  #        :recoverable, :rememberable, :trackable, :validatable
  devise :database_authenticatable, :rememberable, :trackable
  ...

  def password_required?
    false
  end
end

Deviseではログイン画面などデフォルトのビューも提供してくれますが、今回はOpenIDを使った認証にするためビューをカスタマイズします。ビューをカスタマイズする場合は、コントローラーごとカスタマイズする方法と、ビューだけカスタマイズする方法がありますが、今回はコントローラーごとカスタマイズする方法にします。

コントローラーをカスタマイズするにはconfig/routes.rbに追加されたdevise_for:controllersオプションを指定します。以下のように指定するとUsers::SessionsControllerコントローラーを使います。

config/routes.rb:

1
2
devise_for(:users,
           :controllers => {:sessions => "users/sessions"})

コントローラーを作成します。

% ruby1.9.1 script/rails generate controller Users::Sessions

コントローラーをカスタマイズする場合は、ApplicationControllerではなくDevise::SessionsControllerを継承します。

app/controllers/users/sessions_controller.rb:

1
2
class Users::SessionsController < Devise::SessionsController
end

ログインフォームではOpenID用の識別子を入力してもらうようにします。

app/views/users/sessions/new.html.erb:

<%= form_for(resource,
             :as => resource_name,
             :url => session_path(resource_name)) do |f| %>
  <p>
    <label for="openid_identifier" >OpenID URL:</label>
    <%= text_field_tag :openid_identifier %>
  </p>
  <p><%= f.label :remember_me %> <%= f.check_box :remember_me %></p>
  <p><%= f.submit "Login" %></p>
<% end %>

あとは、トップページを準備すれば画面を確認することができます。

トップページ用のコントローラーを生成します。

% ruby1.9.1 script/rails generate controller welcome index
% rm public/index.html

トップページではログインページに移動できるようにします。ログイン時はログイン中のユーザ情報を表示します。

app/views/welcome/index.html.erb:

<h1>Welcome#index</h1>

<% if user_signed_in? %>
  <p>ようこそ<%= current_user.nickname %>さん</p>
  <%= link_to("ログアウト", destroy_user_session_path) %>
<% else %>
  <%= link_to("ログイン", new_user_session_path) %>
<% end %>

サーバを起動します。

% ruby1.9.1 script/rails server

http://localhost:3000/にアクセスすると以下のような画面になります。

トップページ

ログインページに行くと以下のようなフォームになります。

ログインフォーム

OpenID対応

それではOpenIDに対応します。DeviseからOpenIDを使うために、warden-openidを使います。

% sudo gem1.9.1 install warden-openid

Gemfileにも追記します。

Gemfile:

1
gem 'warden-openid'

WargenでOpenIDを使うようにします。

config/initializers/devise.rb:

1
2
3
4
5
6
Devise.setup do |config|
  ...
  config.warden do |manager|
    manager.default_strategies(:openid, :scope => :user)
  end
end

OpenIDの設定をします。warden-openidではOpenIDの認証が成功した時にコールバックが実行され、そこで認証情報に対応したアプリケーション用のユーザを返すことになります。今回は、ここで、ユーザが存在しない場合は自動的に新規ユーザを作成することにします。

config/initializers/openid.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Rails.application.config.middleware.insert(Warden::Manager, Rack::OpenID)

Warden::OpenID.configure do |config|
  config.required_fields = User.required_open_id_fields
  config.optional_fields = User.optional_open_id_fields
  config.user_finder do |response|
    user = User.find_by_identity_url(response.identity_url)
    if user.nil?
      user = User.new
      user.extract_open_id_values(response)
      unless user.save
        message = "failed to create user: "
        message << "#{users.errors.full_messages.inspect}: "
        message << user.inspect
        Rails.logger.error(message)
        user = nil
      end
    end
    user
  end
end

OpenIDの情報とアプリケーションのユーザ情報をマッピングする処理はモデルで行います。

app/models/user.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class User < ActiveRecord::Base
  REQUIRED_FIELDS = {
    :nickname => "nickname",
  }

  OPTIONAL_FIELDS = {
    :email => "email",
    :fullname => "fullname",
    :birth_date => "dob",
    :gender => "gender",
    :postcode => "postcode",
    :country => "country",
    :language => "language",
    :timezone => "timezone"
  }

  class << self
    def required_open_id_fields
      REQUIRED_FIELDS.values
    end

    def optional_open_id_fields
      OPTIONAL_FIELDS.values
    end
  end

  def password_required?
    false
  end

  def extract_open_id_values(response)
    profile_data = {}
    [OpenID::SReg::Response, OpenID::AX::FetchResponse].each do |response_class|
      data_response = response_class.from_success_response(response)
      profile_data.merge!(data_response.data) if data_response
    end
    [REQUIRED_FIELDS, OPTIONAL_FIELDS].each do |fields|
      fields.each do |model_key, profile_key|
        unless profile_data[profile_key].blank?
          self.send("#{model_key}=", profile_data[profile_key])
        end
      end
    end
    self.identity_url = response.identity_url
    self.nickname ||= identity_url
  end
end

一応、ニックネーム情報は欲しいとリクエストしますが、もらえなくてもなんとなく動くようになっています。この状態でログインページにOpenID識別子を入力して、認証に成功するとアプリケーションにログインする事ができます。

ログイン成功

ただし、現在リリースされているruby-openidはRuby 1.9のEncodingに対応していないため、認証中にASCII以外の文字列を含むページにアクセスすることになると失敗します。これを修正する方法はRuby 1.9.1 supportで報告済みですが、まだ取り込まれていません。

まとめ

Rails 3.0 beta4でDeviseを使ってOpenID認証する方法を紹介しました。Rails 3で認証まわりはどうしようか、と考えていている人は試してみるとよいかもしれません。ただ、betaやrcのものを使っているので、これから使い方は変わっていく可能性が高いと考えられます。注意してください。

そういえば、トップページにある会社紹介資料PDFを更新しました。エンジニア紹介ページなどが更新されています。

タグ: Ruby

この記事の続き

2010-07-13

エキスパートPythonプログラミング

数ヶ月前、すでにPythonを知っている人向けのPythonの本が出版されました。オリジナルは2008年に海外で出版されたもので、これはその翻訳です。

エキスパートPythonプログラミング
Tarek Ziade/稲田直哉/渋川よしき/清水川貴之/森本哲也
KADOKAWA/アスキー・メディアワークス
¥ 33,482

内容

さらにステップアップしたいPython開発者向けの内容なので、構文の説明など初級者用の内容はなく*1、どうやってPythonを使うのがよいかを書いてあります。プログラミングだけではなく、開発全体を意識しているのが実務的といえます。パッケージを作り方を説明するところでも、単にEggを作るだけではなく、PyPIに登録するところまで説明しています。他にもよい名前について1章使ったり、テスト駆動開発に1章使ったりと、しっかりと大事な話題はおさえています。ドキュメントについての章があるのもPythonらしいですね。

ただし、幅広く扱っている分、少し物足りない部分もあります。例えば、テスト駆動開発の部分は標準添付のunittestよりもnoseの方により重みをおいて説明した方がより実務的でしょう。

一番有用なのは付録のPython 2とPython 3のUnicode文字列についてまとめたところかもしれません。この部分は日本語版用に翻訳者たちが書き下ろしたもので、マルチバイト文字列を扱う機会の多い日本のPython開発者には特に有用でしょう。Python 2とPython 3は互換性がないため、文字列の扱いを移行するときにこの付録が役に立つことでしょう。

また、オリジナルが出版されてから現在までにPython界隈の状況も変わってきていますが、それについて訳注で補足されているのもうれしいところでしょう。新しく書くコードでは最新の状況にあわせたコードにしたいものです。

まとめ

エキスパートPythonプログラミングの内容を簡単に紹介しました。

エキスパートPythonプログラミングではまったく触れられていませんが、単体テストフレームワークはunittestやnoseよりもPikzieがオススメです。

*1 むしろ最初にインストール方法があるのが違和感。ただ、MinGWなどもインストールするように書いてあるので、初心者用ではない。

2010-07-20

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|