Duplicatable V2.1モジュールの不満点は、実はもう一つだけあります。
それは、せっかく「複製可能」であるのに、各モジュールインスタンスの動作に多態性を持たせることが出来ない、ということです。
もしあるモジュールインスタンスに多態性を持たせたとしても、モジュールアップデートで書き潰されてしまうからです。
その点、Duplicatable V3であれば、多態性を持たせるのは比較的簡単です。というのも、XOOPS_ROOT_PATH/modules/ディレクトリという、最初にアクセスされるべきファイル群はすべて、単なるフックとなっているからです。
サイト毎にカスタマイズ可能を謳うOSSアプリケーションでは、フック処理が良く利用されます。ある特定のファイルを作ると、そちらに動作が移行するというタイプで、ZenCartなどが有名でしょう。
ただ、このタイプだと、どこでフックをしているのか、調べないと判りません。さらに、本体のアップデートでフック処理が置き換わってしまう可能性もあります。(私自身、ZenCartのアップデートで何度もハマったことがあります)
しかし、Duplicatable V3のフックはそれとは逆です。ロジック本体こそが、フック内にあるのです。XOOPSの動作さえ知っていれば、各モジュールについての処理が、XOOPS_ROOT_PATH/modules/に来ることは判っているはずです。そして、それらのファイルこそ、ユーザが書き換えて良いファイルなのです。そのフック処理をやめてしまえば、完全に自分でコントロールできますし、ちょっとだけ処理してからデフォルトのフックをかけることもできます。
これはまさにOOPで言うところの多態性です。XOOPS_TRUST_PATH/modules/内に置いたファイル群が親クラスで、XOOPS_ROOT_PATH/modules/内に置かれた各モジュールが、サブクラスに相当します。
説明の順番が前後してしまいましたが、Duplicatable V3の前提に、XOOPS_TRUST_PATHという重要な概念があります。
XOOPS_TRUST_PATH とは、PHP定数です。XOOPSをある程度触った方ならご存じのXOOPS_ROOT_PATH の親戚のようなものです。この定数は、mainfile.phpで定義します。
define('XOOPS_TRUST_PATH','/home/yourhome/xoops_trust_path');
最初はTinyDという形で、自由にディレクトリ名を決められる、複数インストールができる、という特徴をひっさげて登場したDuplicatable技術ですが、今や一般的になってきました。
ただ、Duplicatable V2.1 は、以下のような弱点を持っています。
1. ディレクトリ名の最後に数字をつける必要がある
モジュール番号を認識するために必要なのですが、このために本当の意味で、「自由なディレクトリ名」とはなっていませんでした。
2. アップデートが大変
複数インストールできる、ということで、TinyDを10も20も入れているサイトがあるようですが、これらのサイトに対して、TinyDをアップデートするとなったら大変です。
すべてのインスタンスについてアップデートしないと、おそらく動作がおかしくなります。
Duplicatable V2.1後、その派生もいくつか出ていて、例えば、templates/やsql/を書き換え可能として、自動的に書き換える、なんてやり方もあります。これであれば、1は解決できますが、今度は2がもっと酷くなります。
ここで登場するのが、Duplicatable V3です。(実はこれ、拙著Customizing XOOPSで、構想は出来ている、とか書いておいたのですが、まったく実装せずに1年も経ってしまいました。)
Duplicatalbe V3 自体、まだ製作途中なのですが、現時点で決まっているのは以下の仕様です。
(1) DocumentRoot外のXOOPS_TRUST_PATHを利用する
(2) XOOPS_ROOT_PATH/modules/下のモジュールディレクトリには基本的にラッパーしか置かない
(3) テンプレートやCREATE TABLEは、onInstallで面倒見る。(XOOPSコアには任せない)
(4) onUpdateで、テンプレートの面倒を見る。
(5) onUninstallで、DROP TABLEを行う。
(6) 管理画面は、?mode=admin という形で処理する
テンプレートやCREATE TABLEを、コアに任せないことで、モジュール番号といった識別子を利用する必要がなくなりました。XOOPS_ROOT_PATH/modules/下に、好きな名前で置くだけです。
複数のモジュールインスタンスが置かれるのは、modules/だけで、しかもここには基本的に中身はありませんから($mydirnameを取得してXOOPS_TRUST_PATH下のメイン処理に渡すだけ)、ほとんど更新の機会がありません。つまり、Duplicatable V3モジュールを10個インストールしていようが、100個インストールしていようが、アップデートするのは、XOOPS_TRUST_PATH内のファイルだけ、となります。
さらにXOOPS_TRUST_PATHを利用することで、直アクセスを想定していないファイルをDocumentRoot外に持ち出すことになります。これはセキュリティ保持上、とても重要です。
これからは、各ファイルの先頭に、
if( ! defined( 'XOOPS_ROOT_PATH' ) ) exit ;
昨日のnewbbの問題でも触れましたが、XOOPSでは、冗長な識別子がよく利用されています。
例えば、newbbでは以下のようなURLになります。
viewtopic.php?topic_id=3656&forum=18&post_id=48548#forumpost48548
実は、このうち、forum(フォーラム番号18)も、topic_id(トピック番号3656)も、冗長な識別子です。唯一有効な識別子は、post_id(投稿番号48548)だけです。
フォーラム番号18以外に、トピック番号3656や投稿番号48548があるわけもなく、
トピック番号3656以外に、投稿番号48548があるわけでもありません。
このような冗長な識別子は、以下の弊害をもたらします。
- URLの一意性を失う
無意味に識別子を増やすことで、識別子の順番がちがうだけのURLが級数的に増えていくことになります。
newbbはこのあたりの作りもきわめて雑で、あるリンクでは、viewmode指定が先に来たかと思えば、別のリンクでは、topic_id指定が先に来たりします。
これはSEOにとっても重大でしょう。
- 権限設定等のミスを招きやすい
まさに今回のnewbbの問題点です。
forum番号がGETで渡されようが、POSTで渡されようが、topic_idやpost_idを対象とする操作であれば、それは冗長情報です。権限がforum単位で設定してあるのなら、かならず、topic_idやpost_idから導き出したforum番号での権限を確認しなければなりません。
ではなぜ、XOOPSにおいては、このような冗長識別子を渡すモジュールが多いのでしょう?
その理由は、イベント通知機能などのXOOPSコア機能との連携のためでしょう。
その具体例として、同じ標準モジュールのmydownloadsを取り上げましょう。
mydownloadsも、singlefile.php において、冗長識別子cidを持っています。「シングルファイル」なのですから、ファイル番号lidだけが、有効な識別子であり、カテゴリー番号cidはまったくの冗長識別子です。
ところが、このサイトにログインして、以下のリンクで表示されるイベント通知オプションを見てください。
lid=65
cid=4&lid=65
両者は同じファイルを指しています。しかし、イベント通知はどうでしょう?
cid指定がある方には、このカテゴリーに関する通知オプションが表示されているのに、lid指定のみの方では、カテゴリーに関する通知オプションはありません。つまりコアのイベント通知機能は、このURLにおけるcid指定からカテゴリー番号が指定されていることを知り、そのカテゴリーについてのオプションも有効にされる、というわけです。逆にlidのみの指定でcid指定がなければ、コアのイベント通知機能はカテゴリー指定を知る術がないため、そのオプションは表示されません。だから、あえて冗長な識別子であるcidをURLに含めて出力するのです。
これは、一見うまい方法に見えますが、実は落とし穴もあります。URLのcidが本当にlidに所属している場合はそれで良いのですが、そうではないカテゴリ番号を指定していた場合でも、特に何の検証もなしに、カテゴリーに関数通知オプションが表示されます。
cid=1&lid=65
このURLで表示されるカテゴリー通知オプションをONにした場合、cid=1に対する登録となってしまうのです!
この不具合は、XOOPSのイベント通知機能の問題とも言えますが、もしあながたモジュール作者であるなら、行うべき対応はとても簡単です。
イベント通知機能などで冗長識別子が必要なケースでは、本来の識別子から冗長識別子を生成して、$_GETにセットすれば良いのです。
例えば、mydownloadsであれば、singlefile.phpの処理の最初の方で、lidからcidを生成して、
$_GET['cid'] = (lidから得られたカテゴリー番号) ;
とすれば、cidがURLに含まれていてもいなくても、イベント通知は完璧に機能します。
これに加えてもう一つだけ貴方がやらなくてはならないことは、冗長な識別子を含んだURLを生成しないようにすることです。実はこれ、とても重要なことです。
とりあえずxhnewbbにおいて、私は可能な限りその作業をしてみました。今度はあなたの番です。
xhnewbbのモデレータ権限についてのバグを取ろうとソースを追っていたら、newbb由来の大きなバグが次々と見つかりました。
- あるモデレータが、全フォーラムを編集できてしまう
- ロックトピックでも自由に投稿できてしまう
- プライベートフォーラムの中身をメンバー以外が自由に閲覧できてしまう
これは、いわゆる「脆弱性」とは違うのですが、*newbb*のアクセスコントロール機能に依存しているサイトでは問題となり得るでしょう。
他にも数多くのバグがみつかったのですが、一応、xhnewbbでは頑張って一通り修正しました。
http://www.peak.ne.jp/xoops/md/mydownloads/singlefile.php?lid=68
ただ、要修正箇所があまりにも広範囲であったため、エンバグしている可能性もあります。一応このサイトも1.17に上げて、しばらく運用してみます。
今回私が見つけた一連の不具合は、*newbb*のアクセスコントロール機能を使っていなければ問題ありませんので、あわててバージョンアップなどを行う必要はないかもしれません。
なお、newbb をベースとした各種モジュールがこれらの不具合に気づいている可能性は低く、いわゆるnewbb系のモジュールの作者は、一応チェックしておいた方が良いでしょう。