Category Archives: Ruby on Rails

How to create OAuth / xAuth Service Provider in Ruby on Rails with devise

At previous entiry I wrote a sample how to support xAuth in Ruby Application.

In this entry, I write how to create the OAuth/xAuth Service Provider in Ruby on Rails project which uses devise plugin for authentication.

OAuth support when you use devise plugin

Installation

First of all, I’m about to write the way of supporting OAuth using OAuth plugin.
pelle/oauth-plugin – GitHub

OAuth plugin supports Rails 3 after version 0.4.0.pre1, and add Gemfile the following line.

gem "oauth-plugin", ">=0.4.0.pre1"

The installation tutorials are written in the github plugin page.

devise plugin support

In addition, to use OAuth plugin in the project which utilize devise, you need to support two methods “login_required” and “logged_in?” Devise uses such helper methods as “authenticate_#{model_name}” and “#{model_name}_signed_in?”, I wrote two helper methods.

[lib/lindoc/oauth_helpers.rb]

module Lindoc
  module OauthHelpers
    def self.included(recipient)
      recipient.extend(ClassMethods)
      recipient.class_eval do
        include InstanceMethods
      end
    end

    module InstanceMethods
      def login_required
        authenticate_user!
      end

      def logged_in?
        user_signed_in?
      end
    end
  end
end

At the controllers associated with OAuth, add this helper methods like the this.

[$RAILS_ROOT/app/oauth_clients_controller.rb]

class OauthClientsController < ApplicationController
  include Lindoc::OauthHelpers
  ...
end

[$RAILS_ROOT/app/oauth_controller.rb]

require 'oauth/controllers/provider_controller'

class OauthController < ApplicationController
  include OAuth::Controllers::ProviderController
  include Lindoc::OauthHelpers
  ...
end

Since I want to use OAuth / xAuth authentication at API of our service, also added :oauth_required filter.

[app/controllers/api/v1/api_controller.rb]

class Api::V1::ApiController < ApplicationController
  include Lindoc::OauthHelpers

  before_filter :oauth_required
  ...
end

Rails nested parameters support

Client applications often post data in nested parameters like “foo[bar]=baz” to Rails application, but OAuth plugin doesn’t currently support this type of parameters. The reason why OAuth plugin doesn’t, is that this plugin doesn’t consider this type when it calculates a signature the way of which is written in the specification of OAuth, and answers a bad/invalid signature. So, you need to add support by overriding the method which helps calculate it.

This solution is written at Issue page of OAuth plugin.
parameter normalisation issues Nesting parameters causes problems.

[config/initializers/oauth.rb]

module OAuth
  module Helper
    # see https://github.com/pelle/oauth/issues#issue/8
    def normalize(params)
      params.sort.map do |k, values|
        if values.is_a?(Array)
          # multiple values were provided for a single key
          values.sort.collect do |v|
            [escape(k),escape(v)] * "="
          end
        elsif values.is_a?(Hash)
          key = k
          values.sort.collect do |k, v|
            [escape("#{key}[#{k}]"),escape(v)] * "="
          end
        else
          [escape(k),escape(values)] * "="
        end
      end * "&"
    end
  end
end]

xAuth support

xAuth is the authentication method which is used in twitter to support desktop/mobile applications. If you want to know how to use xAuth in your applications, you should read this document.
Using xAuth | dev.twitter.com

client application restriction

Like twitter, I want to restrict client applications which can use xAuth authentication, and I add column to decide it.

[db/migrate/xxx_create_oauth_table.rb]

class CreateOauthTables < ActiveRecord::Migration
  def self.up
    create_table :client_applications do |t|
      ...
      t.boolean :xauth_enabled, :default => false

      t.timestamps
    end
    ...
  end
end

/oauth/access_token signature verification method change

In OAuth 1.0 specification, service provider must verify a signature which consists of both Authentication header and GET or POST parameters called “Signature Base String”, and signed by both Consumer Secret and Access Token Secret. This verification step is implemented as filter “two_legged” or “oauth10_request_token” in OAuth plugin, OAuth::Controllers::ProviderController.

[oauth-plugin/lib/oauth/controllers/provider_controller.rb]

module OAuth
  module Controllers
   
    module ProviderController
      def self.included(controller)
        controller.class_eval do
          ...
          oauthenticate :strategies => :two_legged, :interactive => false, :only => [:request_token]
          oauthenticate :strategies => :oauth10_request_token, :interactive => false, :only => [:access_token]
          ...

The simplified difference of these two methods is to use or not to use request token. You need to change signature verification method of /oauth/access_token when you request Access Token by xAuth, because unlike OAuth clients, xAuth clients don’t have request token.

For that reason, to support xAuth, if request has “x_auth_mode=client_auth” in POST parametes which indicate client applications want to authenticate by xAuth, “two_legged” filter should be applied. I used “alias_method_chain” to seperate xAuth and OAuth authentication.

[config/initializers/oauth.rb]

module OAuth
  module Controllers
    module ApplicationControllerMethods
      class Authenticator
        def oauth10_request_token_with_xauth
          if params[:x_auth_mode] == 'client_auth'
            # xAuth authentication
            two_legged
          else
            # OAuth authentication
            oauth10_request_token_without_xauth
          end
        end

        alias_method_chain :oauth10_request_token, :xauth
      end
    end
  end
end

/oauth/access_token user verification

In /oauth/access_token request, you also verify the user using his username and password. If there is a “x_auth_mode=client_auth” POST parameter in request, verify the user and response Access Token.

[$RAILS_ROOT/app/oauth_controller.rb]

require 'oauth/controllers/provider_controller'

class OauthController < ApplicationController
  include OAuth::Controllers::ProviderController
  include Lindoc::OauthHelpers
  ....

  private
  def access_token_with_xauth
    # To use custom failure response with devise, you need the following line.
    # see https://github.com/plataformatec/devise/wiki/How-To:-Provide-a-custom-failure-response-with-Warden
    warden.custom_failure!

    if params[:x_auth_mode] == 'client_auth'
      render_unauthorized = Proc.new do
        render :nothing => true, :status => 401
      end

      # We support screen name and email to login
      user = User.find_for_database_authentication({ :screen_name_or_email => params[:x_auth_username] })
      if user &&
        user.valid_password?(params[:x_auth_password]) &&
        current_client_application.xauth_enabled

        @token = AccessToken.where(:user_id.eq => user,
                                   :client_application_id.eq => current_client_application,
                                   :invalidated_at.eq => nil).limit(1).first
        @token = AccessToken.create(:user => user, :client_application => current_client_application) if @token.blank?

        if @token
          render :text => @token.to_query
        else
          render_unauthorized.call
        end
      else
        render_unauthorized.call
      end
    else
      access_token_without_xauth
    end
  end

  alias_method_chain :access_token, :xauth
end

References

Steak で RESTful API の受け入れテストを書く

Steak は Ruby 製の受け入れテストのためのフレームワークです。
cavalle/steak – GitHub

Github にある説明を読んでいただくとわかると思いますが Steak ではテストは RSpec のように書くことができます。
同様のツールに Cucumber もありますが、Cucumber は英語や日本語で書かなければならないので、自分の中でどうもしっくりしませんでした。
そのため、自分にとっては Steak のようにかけた方がずっと書きやすいです。
ということで、これからは Steak を使って受け入れテストを書いていこうと思っています。

そんな Steak ですが、RESTful API のテストもしたいです。
どうも Steak の裏で動いている Capybara というテストフレームワークは、 visit ‘/’ のように GET することはできるのですが、 POST PUT DELETE というような RESTful API をテストする上ではそれだけでは足りません。

以下のような方法で、POST 等のメソッドを呼び出すことが可能なようです。

$RAILS_ROOT/spec/acceptance/support/helper.rb

module HelperMethods
  def setup_driver
    @driver = Capybara.current_session.driver
  end

  def post(path, params = {})
    @driver.process :post, path, params
  end

  def put(path, params = {})
    @driver.process :put, path, params
  end

  def delete(path, params = {})
    @driver.process :delete, path, params
  end
end

$RAILS_ROOT/spec/acceptance/api/articles_spec.rb

require File.dirname(__FILE__) + '/../acceptance_helper'

feature "Articles" do
  background do
    setup_driver
  end

  scenario 'update article' do
    params = { :article => {} }
    params[:article][:title] = 'foo'
    put('/api/articles/1.json', params)
  end
end

@driver.status_code とかでレスポンスのステータスコードが取得できたりします。
ほかの情報はここなどを読むとよいでしょう。
Class: Capybara::Driver::Base

参考文献

Rails 3 で Paperclip Processor を使用する

Paperclip は高機能なファイル添付のための Rails プラグインです。

thoughtbot’s paperclip at master – GitHub

単純にアップロードが行えるだけではなく、アップロードされたファイルを処理する Processor という仕組みがあります。
Railscasts でも紹介されています。
Railscasts – Cropping Images

この Processor を作成するには Paperclip::Processor というのを継承して、 $RAILS_ROOT/lib/paperclip_processors/hoge.rb みたいな場所に置きます。

この Processor の仕組みが今回必要だったので、 Rails 3 で動かしてみたところ、動いている気配がない。
そこで Processor not loading at first try with Rails3 – Paperclip Plugin というのを参考にして、以下のコードを書いてみたところ無事に読み込まれました。

Hoge::Application.configure do
  config.after_initialize do
    Dir.glob(File.join(File.expand_path(Rails.root), "lib", "paperclip_processors", "*.rb")).each do |processor|
      require processor
    end
  end
end

これを config/initializers/paperclip.rb として置いておきました。

より詳しい Processor の作成方法については以下の blog に非常に詳しく書かれています。
動画をアップロードされたら ffmpeg でサムネイルを作成する方法について書かれていて、非常に参考になります。
Video thumbnails with FFmpeg and Paperclip – Ruby on Rails, JRuby, AWS, EC2, Exalead

注意点としては、 has_attached_file にちゃんと :styles プロパティを設定しておくこと。
これをしないと :processors => [ :hoge ] と書いても、その Processor は呼ばれません。

参考文献

Rails 3 + Haml で production 環境下でもインデントする

Haml をしようするときれいにインデントがされるから好きなんですが、Rails の production 環境下ではインデントがされません。

Haml2.2からはRailsのproductionでインデントが無いと少数派に呼びかけている – komagata [p0t]

速度が問題になるまではインデントしたいので、上記 komagata さんが書かれているように設定したものの、 Rails 3 では上手く動きませんでした。

config.after_initialize でくくらないといけないみたいです。

Hoge::Application.configure do
  config.after_initialize do
    Haml::Template::options[:ugly] = false
  end
end

これを config/initializers/haml.rb に置きました。 1
config/application.rb や config/environments/production.rb の中でもいいですね(たぶん)。

  1. Hoge は適当なアプリケーション名に置き換えること。 []

Redmine を nginx + Unicorn で動かしてみる

今後のために、一度 Webサーバである nginx と Railsサーバ?の Unicorn を使って Redmine を動作させてみることにしました。
すでに Redmine は Apache + Passenger で動いています。

環境

  • Ubuntu Server x86_64 10.04

Rack のアンインストール

このエントリを書いている時点では Redmine は Rails 2.3.5 で作成されていて、 Rails 2.3.5 は Rack 1.0.1 を使用します。
もし Rack の1.2.1とかがインストールされている場合にはアンインストールしましょう。
でないと、こんなエラーが出て起動しません。
これにだいぶんはまりました。

I, [2010-10-05T14:04:25.155622 #26791]  INFO -- : unlinking existing socket=/tmp/unicorn.sock
I, [2010-10-05T14:04:25.155914 #26791]  INFO -- : listening on addr=/tmp/unicorn.sock fd=3
I, [2010-10-05T14:04:25.156344 #26791]  INFO -- : Refreshing Gem list
Missing the Rails 2.3.5 gem. Please `gem install -v=2.3.5 rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.

Unicorn 0.96 doesn’t play nice with Rails 2.3.5

インストール

$ sudo apt-get install nginx
$ sudo gem install unicorn

Unicorn の設定

設定のほとんどは nginx + Unicorn を試してみた – milk1000cc からのコピペです。

ここで設定するソケットを後で使用します。

$ vi $RAILS_ROOT/config/unicorn.rb
# ワーカーの数
worker_processes 2

# ソケット
listen '/tmp/unicorn.sock'

# ログ
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])

# ダウンタイムなくす
preload_app true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{ server.config[:pid] }.oldbin"
  unless old_pid == server.pid
    begin
      # SIGTTOU だと worker_processes が多いときおかしい気がする
      Process.kill :QUIT, File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

設定ができたら Unicorn を起動します。

$ unicorn_rails -c config/unicorn.rb -E production -D

nginx の設定

http にきたものはすべて https に転送するようにしているので、設定は以下のようになります。

upstream unicorn の部分で設定するものは、先ほどの unicorn.rb で設定したソケットです。
最下行から2行目の proxy_pass で設定する部分は、最上位の upstream hoge の hoge になります。

$ vi /etc/nginx/sites-available/redmine
upstream unicorn {
         server unix:/tmp/unicorn.sock;
}

server {
        listen          80;
        server_name     example.com; 
        rewrite ^/(.*) https://example.com/$1 permanent;
}

server {
        listen  443;    
        server_name     example.com;

        root            /var/www/redmine/public;
        error_log       /var/www/redmine/log/error.log;

        ssl  on;
        ssl_certificate  /etc/nginx/ssl/apache.pem;
        ssl_certificate_key  /etc/nginx/ssl/apache.pem;
        
        ssl_session_timeout  5m;
        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;

        location / {
                if (-f $request_filename) { break; }
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_pass http://unicorn;
        }
}

サーバのスタート

$ sudo ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/redmine
$ sudo /etc/init.d/nginx start

参考文献

devise で Basic 認証をする

devise は高機能な Rack ベースの認証システムだそうです。
plataformatec’s devise at master – GitHub

よくあるログイン画面を表示して認証する方式もありますし(これはなんというのだろう?)、いろいろな認証方式を使用することができるようです。

今回、お試しサイトを作る必要があって Basic 認証を使用することにしました。
その方法をググってもいまいちわからなかったので、ここに記載しておくことにします。
しかしながらこれで使い方があっているのかどうか、いまいち自身がないので間違っている場合には、ご指摘をお願いします。

devise では通常以下のようなコードで認証を必須にできます。

class ArticlesController < ApplicationController
  before_filter :authenticate_user!
end

以下のページによると HTTP を使用した認証にはこんな風に書いてあります。
How to: use http authentication – devise – GitHub

def http_authenticate
  authenticate_or_request_with_http_digest do |user_name, password|
    user_name == "foo" && password == "bar"
  end
  warden.custom_failure! if performed?
end

今回はパスワードは固定ではないので、えいやとこれだけにしてみました。

class ArticlesController < ApplicationController
  before_filter :authenticate_user!

  def http_authenticate
  end
end

思った通りに Basic 認証で認証することができるようになりました。