partuzaのガジェット追加のシナリオ
Partuza のソースを追うに当たって、2-legged oauthは納得したので後は実際のユーザーストーリーの流れに即してコードを追うことに。
ということで、手元にインストールして試してみたメモをベタバリ(インストールログじゃないです)、したものを徐々に編集しつつ。
Story 0. End User がガジェットを追加する。
エンドユーザーはGadget定義であるXMLファイルのURIを直接指定して自分のページにガジェットを追加します。iGoogle などだとURIを直接書くのではなくクリックして選べるようになってますが、要するにどのXMLファイルを使うか、ってことなので、partuzaだと、URIを直接指定できるようです。
とりあえず、試しにCouchDBでホストとしている以下のXML定義を読ませます。
http://webjourney.local/webjourney-default/_design/webjourney/gadgets/sticky.xml
このガジェット、HTML/Markdownで編集可能なメモガジェット用に使うつもりでいるのですが、、それはおいといて、次のような実装になっています。
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Sticky"> <Require feature="opensocial-0.9" /> </ModulePrefs> <Content type="html"> <![CDATA[ <div id="content"></div> <script type="text/javascript"> var req = opensocial.newDataRequest(); req.add(req.newFetchPersonRequest('VIEWER'), 'viewer'); req.send(function(response) { document.getElementById("content").innerHTML = "Fetched Data: <br/>" + JSON.stringify(response.get('viewer').getData()); }); </script> ]]> </Content> </Module>
newDataRequest() で 実際にviewer(End User)のデータをとってこよう、というわけです。
Story 1. Container(Partuza) がガジェットをDBに登録する。
End User がガジェットを追加すると、HTTP GET(ダサイ) で /profile/addapp?appUrl={追加するGagetのURI} がリクエストされ、Container側に制御が映ります。
Containerはまず、partuza/Application/Controllers/profile/profile.php から addapp($params) メソッドを呼び出します。ちなみに、$_SESSION["_id"] にはログインユーザーのIDが格納されているようです。
addapp から、partuza/Application/Models/applications.php の get_application($appUrl) に制御が映ります。application 情報を取得する、ということですが、このメソッド呼び出し、実はPartuza固有のキャッシュ機構が働いていて、実際に呼び出されるのは load_get_application($appUrl) のようです。ここで、実際に、$appUrl にアクセスしてXMLを取得し、その辺の情報をデータベースに格納します。appUrlの単位に登録されているようです。get_applicationは最終的に アプリケーションのIDを含むHashを返します。
続いて profile.php のaddapp($params) に制御が戻り、ここでは、上記のHashからIDを取得し、登録者のid ($_SESSION["id"]) とアプリケーションのID のひも付けをデータベースに保存するようです。
この作業が終わるとHomeビューやProfileビューにガジェットが表示されるようになるようです。
Story 2. ContainerがガジェットのIframeを用意する
HomeビューやProfileビューにガジェットが表示されるわけですが、実体はiframeで、Gadget Rendering Server(Shindig)のほうにリクエストを発行するiframeをセットします。
Homeビュー(http://partuza/home)にアクセスしたときは、partuza/Application/Controllers/home/home.php の index($params) メソッドを呼び出します。partuza/Application/Models/applications.php の get_person_applications($_SESSION["id"]) を呼び出してStory 1で登録済みのアプリケーション一覧を取得し partuza/Application/Views/profile/home.php を使ってレンダリングします。
個々のgadgetのテンプレートは Views/gadget/gadget.php にあるようです。詳細はおい説いて、ひとまず、次のようなiframeを生成することに注目します。
<iframe src="http://shindig/gadgets/ifr?synd=default&container=default&viewer=1&owner=1&aid=2&mid=3&country=US&lang=en&view=home&parent=http%3A%2F%2Fpartuza&st=TWF1QTRyWXhBY1Awd0pmazFWQWxaM0UxSUdvOFlHMmNXWFdZemhLJTJGcnlIOWp1VzFXMndQM0V3MTdwYm8wREolMkZBRmp1MmFkelVFUzRMZDNVSDZvdnFFM21seVpJN0xQaU9Na3ZOdmhRUjV3RVJSUjJMRlglMkZOcThSRUlmZ1c4ZlVEd0Q0SW1Eb20xJTJCcDhBSlRXUEtiWDJLdzJZbkdEOGVXaHV6VkdOM1VHSHg1UWJjZUxQeGh4cGVjdG9xSnM3SlYyTFZlUjFaVmduUk5Mc29MSkphYzVNUlJiR3B1aWszNWJWR3ltUEFaRm1FdldXYkU1T25SNE5HNXRmVTBycU9HY3NmZmZnJTNEJTNE&v=17de9532397c76282440e36585c36d73&url=http%3A%2F%2Fwebjourney.local%2Fwebjourney-default%2F_design%2Fwebjourney%2Fgadgets%2Fsticky.xml#rpctoken=1476043344 "> </iframe>
ちょっと長いですが、いろいろパラメーターがくっついています。このパラメーターはShindigの実装に依存するようで、OpenSocialの規格にはContainerからGadget Rendering Server のリクエストパラメーターのスペックなんてなさそうなんですが、どうなんでしょう(ちゃんと確認していない)。
ざっと表にすると以下のようになります。
key | value |
---|---|
aid | 2 |
container | default |
country | US |
lang | en |
mid | 3 |
owner | 1 |
parent | http://partuza |
st | TWF1QTRyWXhBY1Awd0pmazFWQWxaM0UxSUdvOFlHMmNXWFdZemhLJTJGcnlIOWp1VzFXMndQM0V3MTdwYm8wREolMkZBRmp1MmFkelVFUzRMZDNVSDZvdnFFM21seVpJN0xQaU9Na3ZOdmhRUjV3RVJSUjJMRlglMkZOcThSRUlmZ1c4ZlVEd0Q0SW1Eb20xJTJCcDhBSlRXUEtiWDJLdzJZbkdEOGVXaHV6VkdOM1VHSHg1UWJjZUxQeGh4cGVjdG9xSnM3SlYyTFZlUjFaVmduUk5Mc29MSkphYzVNUlJiR3B1aWszNWJWR3ltUEFaRm1FdldXYkU1T25SNE5HNXRmVTBycU9HY3NmZmZnJTNEJTNE |
synd | default |
url | http://webjourney.local/webjourney-default/_design/webjourney/gadgets/sticky.xml |
v | 17de9532397c76282440e36585c36d73 |
view | home |
viewer | 1 |
ここで重要となるのがstで渡されているSecurity Tokenと呼ばれるものです。
Shindigでは、GadgetとShindig間の通信の妥当性検証を行うために、stパラメータ値が使わ
れます。これは、"OwnerID:ViewerID:AppID:Domain:AppUrl:ModuleID"
という文字列で構成されるのですが、このstパラメータ値は暗号化された文字列が送受信される
http://groups.google.co.jp/group/opensocial-japan/browse_thread/thread/5db84f55b970630a?pli=1
ことになります。
だそうです。何が妥当で何が妥当でないのかはともかくとして*1、このst値は、GadgetからShindigに対して発行されるデータリクエストに使われるようです。これまでOAuthが云々、と調べてきたのですが、なんという、Container -> Shindig(Renderer) -> Shindig(REST/JSONRPC API)感では、OAuthじゃなくて、このst値でアクセス制御をやっていたようです(涙目)。
つまり、3週間ほど前に書いた、
src/social/servlet/ApiServlet.php から。
(略)
} // else, not a valid oauth request, so don't botherhttp://d.hatena.ne.jp/yssk22/20090801#1249143606
で、この not a valid oauth request のほうで処理しているようなんですよ、これが。
ちなみに、Partuza のほうで、st値を生成していますが、
$securityToken = BasicSecurityToken::createFromValues( isset($vars['person']['id']) ? $vars['person']['id'] : SecurityToken::$ANONYMOUS, // owner isset($_SESSION['id']) ? $_SESSION['id'] : SecurityToken::$ANONYMOUS, // viewer $gadget['id'], // app id PartuzaConfig::get('container'), // domain key, shindig will check for php/config/<domain>.php for container specific configuration urlencode($gadget['url']), // app url $gadget['mod_id'] // mod id );
具合で、st値を作成しています(BasicSecurityTokenの実装ではSHA1による暗号化(+base64encode)が行われていました)。
このst値は、実際には timestamp なども含まれていて、RESTService側でExpireチェックできるような仕組みになっていました。
つまり厳密には Gadget と Shindig 間ではなく Gadget Client(Partuza) と REST API (Partuza) 間の妥当性検証のために使われているように見えますが、どうなんだろう。Shindig が具体的に st の検証をしているようなコードは見あたらなかった、というか暗号化しなくてもいいし、してもshindig単体では復号できないし。
ということで、OAuthはひとまず後回しにして、このstの取り扱いの実装をすればいいんじゃん、ということに3週間ほどして気がついたorz
*1:おそらく、正当なContainerで生成されてたtokenであることを検証するのかと