OAuth認証でmultipart/form-dataをPOST

oauth gemでマルチパートのPOSTは単体ではできないようなので、multipart-postと組み合わせる方法と、OAuthプロバイダ実装時の注意点について。

各gemは以下のコマンドでインストールできる。

$ sudo gem install oauth multipart-post

ポイントはマルチパートリクエストにOAuth::AccessToken#sign!を適用すること。

require 'oauth/consumer'
require 'net/http/post/multipart'

OAuth::VERSION = 1.0

consumer = OAuth::Consumer.new('consumer_key', 'consumer_secret',
                               :site => 'http://provider.example.com',
                               :request_token_path => '/request_token',
                               :authorize_path => '/authorize',
                               :access_token_path => '/access.token')

request_token = consumer.get_request_token(:oauth_callback => 'http://consumer.example.com/callback')

puts request_token.authorize_url

access_token = request_token.get_access_token

File.open("./image.jpg") do |image|
  req = Net::HTTP::Post::Multipart.new(url.path,
                                       'text' => 'hi, there',
                                       'image' => UploadIO.new(image, "image/jpeg", "image.jpg"))
  access_token.sign! req
  res = Net::HTTP.start('provider.example.com', 80) do |http|
    http.request(req)
  end
end

僕はOAuthプロバイダを実装していて、そのテストスクリプトとして上記を実行している。
その際に注意が必要だった点として、マルチパートの各フィールドはSignature Base Stringに含めてはならないことが挙げられる。
上記のスクリプトは正しくtextやimageをSignature Base Stringに含めないが、プロバイダサイドで通常のリクエストとマルチパートリクエストを抽象化して扱っていたため、textを含むSignature Base Stringを生成していた。
そのため、マルチパートリクエストのSignatureが一致しないため401が返るという問題が起きる。

この問題を含むSignatureに関連したバグの調査にはコンシューマとプロバイダのSignature Base Stringを比較する必要があるが、oauth gem側では簡単にSignature Base Stringを取り出す方法がないかもしれない。
(以下の方法を試したが、出力内容は純粋なSignature Base Stringではない)

  res = Net::HTTP.start('provider.example.com', 80) do |http|
    puts req.signature_base_string(http, consumer, access_token)
    http.request(req)
  end

また、oauth gemはRFC 5849 - The OAuth 1.0 Protocolで定義されないoauth_body_hashというパラメタを付加する。これはOAuth Request Body Hashという拡張のようだ。http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
これもプロバイダ側でSignature Base Stringに含める必要がある。

まとめ1:oauth gem + multipart-post = うごく
まとめ2:OAuthプロバイダはSignature Base Stringにマルチパートのフィールドを含めるな&コンシューマの拡張に寛容に

CrunhGear/TechCrunch Meetup in Tokyoに行ってきた

Ba Da Da Ding Ding Ding Ding Ding. Greetings from the Land of the Rising Sun, Hello Kitty, Ninjas and all sorts of sexual fetishes. Serkan and I are talking about having a meetup here in Tokyo on Tuesday night.

数日前にTechCrunchで告知されたこのイベント,当初20+を予定していたところ,当日は100人以上集まったらしい.
id:yuisekiさんやid:Ubuntuさんもいらっしゃっていた.

会場に来ていた方はテクノロジ企業に勤めている方,日本で働いている外人,いわゆるTechCrunchされたいStartupの人たちが主だった.

勉強不足でなかなか英語で中身のある会話はできなかった.

Steve Jobsの容態

http://www.apple.com/pr/library/2009/01/05sjletter.htmlによると,Jobsはやっぱり病気してたそうな.

Fortunately, after further testing, my doctors think they have found the cause―a hormone imbalance that has been “robbing” me of the proteins my body needs to be healthy. Sophisticated blood tests have confirmed this diagnosis.

って,具体的な病名を隠してるが…….

So now I’ve said more than I wanted to say, and all that I am going to say, about this.

結びも強気だ.


一日も早い回復をお祈りします.

render :partial => ..., :collection => ... でコレクションの要素のインデックスを取得する方法

他に誰一人としてirc #rubyonrailsで赤っ恥を書かないようにメモ.

render :partial => 'tasks/task', :collection => tasks

こういう風に書いた場合,パーシャルの内部では次のようなローカル変数が定義されている.

<%= task_counter %>

ふと思いついたのでCPS

リスト構造の中のノード数を数える手続きを考える.

(define node-count
  (lambda (tree)
    (if (pair? tree)
	(+ 1 (node-count (car tree)) (node-count (cdr tree)))
	1)))

当然末尾再帰になってないからスタックがあふれる.

これをCPSに変換するのはちょっとめんどくさい上に読みづらい.

(define node-count-cps
  (letrec ((node-count-iter
	    (lambda (tree k)
	      (if (pair? tree)
		  (node-count-iter (car tree)
				   (lambda (x)
				     (node-count-iter (cdr tree) (lambda (y) (k (+ 1 x y))))))
		  (k 1)))))
    (lambda (tree)
      (node-count-iter tree (lambda (x) x)))))

どうにかならんのやか?

functional testでセッションの値を設定する方法

ちょっとメモ.restful_authenticationプラグインのAuthenticatedSystemをインクルードしたコントローラHomesControllerでlogin_requiredメソッドをbeforeフィルタとして宣言している.users.ymlでsomeoneというユーザが定義されているとき,session[:user_id]にsomeoneのidを代入してテストするには.

get :homes, nil, :user_id => users(:someone).id

第3引数のハッシュがsessionになる.