MySQL user Folder の README

以下の文書は Zope の ユーザ管理を MySQL データベースを使っておこな うためのプロダクト MySQL user folder 0.9.1 のアーカイブとともに配布されている README の翻訳です。

mysqlUserFolder

この Zope プロダクトは次の4つの機能を備えています。

  • 主な機能は MySQL サーバのデータを使って、ユーザの認証とロールの割当てをおこなうことです。
  • セッションの管理をおこないます(ロギングもサポート)。
  • ユーザやセッションのデータをカスタマイズして読み書きすることが可能です。これらの情報は MySQL のテーブルに保存されます。
  • ユーザのアカウント情報を変更するためのメソッドを提供しています。Anonymous ユーザの作成も可能です(この機能により、ユーザ自身が Web 経由でアカウントを登録することが可能になります)。また、フォルダの管理インターフェースを使ったユーザ管理も可能です。

このユーザフォルダは HTTP 認証とクッキーを使った認証のどちらもサポートしています。セッションはクッキーを使っている場合にのみ利用可能です。

このバージョンは Zope 2.6.1 環境で動作確認をおこないました。このプロダクトを使うには python モジュールの MySQLdb (バージョン 0.9.2 で動作確認)と MySQL (バージョン 3.23.52 で動作確認)が必要です。なお、mysqlUserFolder は COMMIT/ROLLBACK 命令を使わずに作成されています。

メーリングリストの URL は http://www.egroups.com/group/mysqlUserFolder で、私のメールアドレスは vladap at criticalpublics dot com です。

セキュリティ: 注意事項

ユーザ自身にアカウント情報の変更や新規登録を許可する場合は「ユーザインタフェース」の項によく目を通してください。

dtml.user にある DTML スクリプトはサンプルに過ぎません。実際に使うには入力(anonymous ユーザの作成、email、realname フィールド等)のチェックを付け加える必要があります。

mysqlUserFolder は SQL クエリの生成に python DB API パラメータを利用しています。このため SQL クエリにおける特殊文字は適切にエスケープしなければなりません。また、"manage" DTML スクリプトはhtml_quote タグを使っています。。

しかし上記の入力チェックをしても、それだけは充分でありません。信頼できるユーザばかりではない場合、"user" DTML スクリプトにおいても入力チェックをおこなう必要があります。なおサンプルの "user" DTML では html_quote タグを使っていないことに注意してください。

Zope の認証プロセス

Zope はオブジェクト階層から(ユーザフォルダの) 'acl_users' という id を検索し、最初に見つかったオブジェクトを使って認証(および権限付与)をおこないます。ユーザフォルダが見つかると、Zope は HTTP リクエスト(REQUEST)オブジェクトとロールのリストを使ってユーザフォルダの評価用メソッドを呼び出します。REQUEST オブジェクトはすべてのユーザのクッキーを保持しており(クッキーは HTTP のリクエストの度に送られてきます)、クライアントが HTTP 認証データを送ってきた場合は、その内容(ユーザ名とパスワード)も保持されています。Zope は評価用メソッドに対し「このリクエストをリスト中のロールのひとつとして扱っても良いか?」とたずねます。ユーザフォルダはこの問い合わせに対し、ユーザオブジェクトまたは None を返します。ユーザオブジェクトが返ってきた場合、Zope は REQUEST オブジェクトの中にユーザオブジェクトを含め、それを最初に呼び出されたオブジェクトに渡します。 None が返ってきたとき、Zope は次のユーザフォルダを検索します。

HTTP 認証においてはすべてがシンプルです。REQUEST オブジェクトにユーザ名とパスワードが含まれていない(あるいはそれらが間違っている)場合、ユーザフォルダは None を返します。REQUEST の評価ができなかった場合、Zope はクライアントへエラー("403 Authorization required")を返し、それを受け取ったクライアントはユーザ名とパスワードを入力するダイアログを表示します。この仕組みは mysqlUserFolder を使っていて Zope スーパユーザのユーザ名とパスワードを入力した場合も正しく動作します。この場合、ユーザフォルダは None を返しますが、ルートにあるユーザフォルダによって認証されるためです。

クッキー(とフォーム)を使った認証はこれほど簡単ではありません。問題は(クッキーによる認証が失敗した場合) HTTP エラー以外にその内容をクライアントに通知する方法がないことです。REQUEST を評価できなかった場合、 mysqlUserFolder はログイン画面を表示するための例外を発生させる必要があります。このとき mysqlUserFolder はログイン画面を表示させるだけで、 None を返すことはできません。mysqlUserFolder で認証できなかった場合、他のユーザフォルダを使って認証させることができないのです。つまりこれは mysqlUserFolder でクッキーを使った認証を使う場合、Zope のスーパーユーザ(emergency または init ユーザ)のユーザ名とパスワードを使ったアクセスができなくなることを意味します。このため、mysqlUserFolder データベースに必ず "manager" ロールを登録しておく必要があります。

ユーザ認証、セッション、クッキー

mysqlUserFolder は認証(およびほかのすべての機能)を MysQL テーブルのデータを用いておこないます。ユーザ認証はクッキーと HTTP 認証の両方をサポートしています。クッキーモードでは認証と同時にセッションの管理もおこないます。mysqlUserFolder が管理するセッションは独自のもので、Zope 自体のセッション管理とは別物です。

クッキー認証はランダムなトークンを使って実装されています。トークンには2つのパラメータ、time to live と timeout があります。前者の値が有効な場合は絶対的な有効期限を表し、後者はクッキーを有効なものとして保持するアクセス時間の範囲を表します。ユーザとクッキー・トークンのパラメータは管理画面で変更できるようになっています。

各トークンの実体は2つのクッキーになっており、ひとつはトークンの id (ユーザまたはセッションの id)を保持し、もうひとつは評価用のランダムな値を保持しています。つまりユーザとセッションを管理するには4つのクッキーが必要になります。セッション管理のクッキーとクッキー管理のクッキーは両方とも永続性の有効/無効(ブラウザ終了の際クッキーを保存させるかどうか)を設定できるようになっています。

デフォルト設定でクッキー名はレルム(realm)に左右されないようになっています。このため、ユーザフォルダのクッキーは別のユーザフォルダによって上書き可能になっています。つまり、同じドメイン名で異なる複数のレルムを使うことはできないようになっています。この設定は "Use realm for cookie names" のオプション設定で変更可能です。このオプションを設定するとクッキー名にレルムが含まれるようになります。なおこの場合、レルム名はクッキー名として有効な文字だけを使って設定しなければなりません。

セッション管理はクッキー・トークン使って実装されています(このタイプのトークンは必ず timeout を設定しなければなりません)。ユーザが mysqlUserFolder で保護されたリソースにアクセスしようとしたとき、有効なセッションがなければ新たなセッションが生成されます。セッションは mysqlSession として表わされ、REQUEST ['Session'] のように REQUEST オブジェクトを通じてアクセスできます。

ユーザフォルダでセッション認証をおこなう場合、ユーザの認証がおこなわれた後、データベース上のユーザ id (ユーザ名ではない!)がセッションに割当てられます。その後はセッション・クッキーが有効である限り、セッション・クッキーだけを使って認証がおこなわれます。セッションが無効(タイムアウト発生、または有効期限に達した状態)になると、mysqlUserFolder は再びユーザ認証にクッキーを使おうと試みます。

HTTP 認証を使っている場合、ユーザ認証にセッションが使われることはありません。

mysqlUserFolder にはポートのリストを指定するためのパラメータがあります。このパラメータに指定されたポート経由のリクエストがあると、 mysqlUserFolder はクッキーの設定にかかわらず、常にユーザ名とパスワードによる認証がおこなわれます。たとえばユーザフォルダでクッキーを使う設定にしていても、FTP で同じ認証を使うことが可能になります(FTP サーバは HTTP と異なるポートを使います)。

MySQL データベース

mysqlUserFolder を使うためにはパラメータとして指定されるすべてのテーブルがあらかじめデータベース中に作成されていなくてはなりません。そのほかの接続パラメータは管理画面から設定します。

realm は同じテーブル上で異なるユーザフォルダを扱う場合に使用します。PasswordType にはパスワード・フィールドに用いるパスワードのタイプを指定します。現時点で指定可能なのは 0 (平文パスワード)または 1 (UNIX crypt)のいずれかです。

Session、Token および User テーブルは MySQL のオートインクリメント id を使って参照されます(username と relm のペアは使われません)。

Token テーブルはセッションおよびユーザの認証に使われます。テーブルのレコードとして保存されるのはランダムな文字列で、クライアントからクッキーのフォームとして送信されるべきデータです。このデータにより有効なセッションであるか、または有効なユーザであるかどうかを判別します。Class フィールドは Id の種類を示します。Class が 'session' となっていれば、それはセッションの Id で、'user' となっている場合はユーザの Id を意味します。Token テーブルのプライマリキーは (Id, Class) です。

レコードの参照整合性についてよく注意する必要があります。セッションのレコードを削除する場合は、Token テーブルから同じ Id を持ちかつ Class の値が 'sssion' となっているレコードを同時に削除しなくてはなりません。ユーザを削除する場合はすべての関連するセッションとトークン、つまり Id がユーザの Id となっていて Class が 'User' となっているものを削除する必要があります。mysqlUserFolder の管理画面を使ってユーザを削除する場合、これらの処理は自動的におこなわれます。

Session と Token テーブルには随時データが追加されるため、定期的にクリーンアップする必要があります。

キャッシング

mysqlUserFolder には何度も同じ SQL 問い合わせを発しないようにセッションとユーザのデータをキャッシュする機能が備わっています。しかし、ユーザフォルダはランタイム永続性データを(ZODB に)一切保存しないように設計されているため、マルチスレッド/ZEO 環境において安全に保持できないキャッシュデータがあります(非永続的なデータはスレッドごとに隠蔽されます)。

cfg.py には6つのキャッシュ制御用変数、CACHE_SESSIONS、CACHE_SESSIONS_LIFE、CACHE_USERS、CACHE_USERS_LIFE、CACHE_TOKENS、CACHE_TOKENS_LIFE があります。LIFE と名の付くパラメータにはキャッシュに保持する時間を設定します。

CACHE_SESSIONS を有効にすると、認証済みユーザのセッションだけがキャッシュされます(ユーザフォルダでクッキーを使うように設定すると認証もキャッシュされます)。この場合、セッションのテーブルからセッションデータを削除しても、LIFE に指定した時間内はセッションが有効なままとなります。

CACHE_USERS を有効にすると、ユーザ名、id、パスワードがキャッシュされます。ただし、パスワードが違っている場合、mysqlUserFolder はユーザ情報をキャッシュせずにリフレッシュします。

CACHE_TOKENS はクッキーの中のトークンをキャッシュするために使います。この設定はセッションとユーザ両方のクッキーに対し適用されます(mysqlUserFolder が両方を使うように設定されている場合)。トークンのパラメータは timeout と life の2つがあります。キャッシュを使用した場合、スレッドはトークンをデータベースから読み込んだときにだけアクセス時刻のアップデートをおこないます。このため、TOKENS_CACHE_LIFE は TOKEN_TIMEOUT よりはるかに小さな値を設定する必要があります。

キャッシングに関し重要な点は以下の通りです。

  • ユーザおよびセッションのトークンはタイムアウト時間に達する前に破棄されるようにしなければなりません(ただし、その差は TOKENS_CACHE_LIFE に設定した秒数を越えてはいけません)。
  • ユーザおよびセッションのトークンをデータベースから削除しても、TOKENS_CACHE_LIFE に指定した時間内はキャッシュに残ります。
  • パスワードを変更した場合、古いパスワードが CACHE_USERS_LIFE の時間内キャッシュに残ります。新規パスワード登録の場合は問題ありません。
  • ユーザを SQL データベースから削除しても CACHE_USERS_LIFE の時間内キャッシュに残ります(セッションも同様)。
  • ユーザを削除した後、同じ Id で別のユーザを登録した場合、どんな不具合が起きても不思議ではありません。

永続性、スレッド、テーブルのロック

mysqlUserFolder で使用しているオブジェクトはどれも(ZODB レベルの)永続性はありません。このため python レベルのロックも必要ありません。複数の MySQL テーブルをまたいだアクセスや更新がある場合は MySQL のロックが使われます(単一のクエリがアトミックなオペレーションになります)。

SQL エラーの場合、デッドロックが発生する可能性があります(Zope のスレッドがデータベースをロックしたままになります)。ただし、エラーが発生するのは操作中に SQL サーバへの接続が切れてしまった場合に限られるはずです。

クエリの実行に失敗した場合、mysqlUserFolder は次のクエリを発行する前に自動的に再接続し直すようになっています。

ユーザとセッションに関連するそのほかのデータ

mysqlUserFolder は接続ユーザとセッション情報のカスタマイズが可能になっています。データは SQL テーブルに保存されます。ユーザおよびセッションのオブジェクトは以下のメソッドを持っています。

    setMiscData (string_key, (int_value, string_value)) 
    (int_v, string_v) = getMiscData (string_key)

たとえば u_UserPage.dtml でユーザとセッション毎の訪問数を保持させたいときなど、この機能が使えます。なお、Zope 標準のユーザフォルダが返すオブジェクトにこのメソッドはありません。u_UserPage.dtml 側でオブジェクトが mysqlUser かどうかをチェックするようにしてください。

ユーザインタフェース

mysqlUserFolder はユーザ(およびフォルダ)の管理用に manage_* と user_* という2組の関数群を提供しています。manage_* 関数はユーザフォルダのパラメータ変更とユーザの管理をおこなうためのもので、"Manage" パーミッションにより保護されています。

user_* 関数として用意されているのは、user_login ()、user_logout ()、user_change_other_data ()、user_change_password ()、user_create () です。

最初の2つの関数は誰でもアクセス可能ですが、mysqlUserFolder がクッキー認証を使うように設定されていない場合はエラーが発生します。

2つの user_change_* 関数はユーザが自分自身のデータを変更できるようにするためのものです。user_create () は Anonymous ユーザが新規ユーザの登録を可能にするための関数です。これらの関数は 'Edit MySQL User Data' の設定により保護されています。mysqlUserFolder でユーザ登録の許可が与えられていない状況で Anonymous ユーザが method user_create () を実行した場合はエラーが発生します。

user_change_other_data () と user_change_password () はログイン中のユーザが自分自身の情報を変更するためにのみ使う関数です(ユーザは REQUEST オブジェクトの情報で判別)。

mysqlUserFolder にはフォルダ機能利用のインターフェースとして、6つの DTML メソッドが用意されています。

docLogin
クッキー認証を使っているときに、ログイン画面の表示をおこないます。ユーザフォルダはユーザの認証が必要な場合、(例外を発生させることで)このメソッドを表示します。
docUserPage
デフォルトのユーザ用ページ。他のパスが設定されていない場合、ログイン後のユーザはここにリダイレクトされます。
docNewUser
(Anonymous ユーザによる)新規ユーザ登録用のサンプルフォーム。
actChangeOther
ユーザ情報変更のため user_change_other () を呼出すメソッド。
actChangePassword
ユーザのパスワード変更のため user_change_password () を呼出すメソッド。
actCreateUser
新規ユーザ登録のため user_create () を呼出すメソッド。

docLogin に関する注意: このページは(ログインに失敗したときやユーザが直接アクセスしてきた場合に表示する)普通のページとしても、例外の内部で呼出すテンプレートとしても利用可能です。アクセス制限されたリソースにユーザがアクセスしようとすると mysqlUserFolder は docLogin を値として持つ例外を発生させます。その後 Zope は standard_error_message を呼び出し、このメソッドが(デフォルトで)表示されます。しかし、同時に HTML header/footer も表示されることに注意してください。例外を発生させた場合、 mysqlUserFolder はパラメータ "exception_raised" を docLogin に渡します。これによってメソッドは standard_error_message を通じて確実に正しい応答を返せるようになっています。

act* メソッドは内部で user_* メソッドを呼び出すため、実行には "Edit MySQL User Data" の許可が必要です。デフォルトでこの許可は与えられていないため、実行できません。通常のユーザにこれらの操作を許可するには、次のいずれかの設定をしておく必要があります。

  1. 'Anonymous' に対し許可を与える。この方法はユーザが act* のチェックをスキップして直接 user_* メソッドを呼出せてしまうため、安全ではありません。
  2. act* に proxy ロールを設定する(推奨)。こちらの方法は user_* の直接実行ができず、act* DTML メソッドにより厳密なチェックがおこなわれるため、より安全です。proxy ロールの設定手順は以下の通りです。

    1. 'sysScriptRole' などの名前で特別なロールを登録し、このロールを Manager ロールを持つユーザに追加します(Zope をリスタートするとキャッシュがクリアされます)。
    2. 'sysScriptOwner' などの名前で特別なユーザを登録し、前述のロールをこのユーザにも割り当てます。このユーザには(任意の)容易には推測できないパスワードを設定してください。
    3. この内容を "Advanced" タブの "Setup user action methods" フォームに入力します。これにより、act* メソッドのオーナーが変更され、proxy ロールには先に作成したロールが割当てられます。同時にこのロールに 'Edit MySQL User Data' の許可が与えられます。

どちらの方法を使用したときも、ユーザが変更できるのは自分自身のデータだけです。しかし a の場合、ユーザは act* メソッド内部でおこなわれるチェックをスキップしてしまうことも可能です。

act* メソッドに厳密な入力チェックを付け加えることも非常に重要です。 mysqlUserFolder が標準で提供している DTML メソッドはいずれもサンプルに過ぎません。一般的なアクセス制御に用いる場合も用途に合わせて変更する必要があります。

このほかのセキュリティ設定では docUserPage メソッドへのアクセスを変更する必要があるかもしれません。docUserPage の "View" は "mysqlRole" にだけ許可すべきです。こうしておくとユーザが直接ユーザページにアクセスしようとしても、Zope が認証を要求するようになります。docUserPage メソッドの表示には REQUEST に mysqlUser オブジェクトが含まれていることが必須となっているため、ユーザ認証が必ず要求されるのです。

設定パラメータ VALIDATE_

cfg.py の VALIDATE_ パラメータは認証プロセスを変更するためのものです。

VALIDATE_ALWAYS_SUPER を 1 に設定すると、mysqlUserFolder は常にスーパーユーザに対する認証だけをおこないます。この機能を使うためにはあらかじめ Zope の emergency ユーザを登録しておく必要があります。この認証は MySQL サーバへ接続せずに動作します。

VALIDATE_ALWAYS_ANONYMOUS を 1 に設定すると、mysqlUserFolder はクッキーや HTTP 認証を使わず、ユーザを Anonymous ユーザとして認証します。

リスト VALIDATE_IGNORE_ROLES には SQL テーブルには定義されていても mysqlUserFolder に表示させたくないロールを定義します。

フォルダの設定パラメータ

基本的なユーザフォルダのプロパティ(操作のモードやデータベースの接続に関するものなど)はフォルダ作成時に定義されますが、作成後も Properties から変更できるようになっています。

クッキーとロギングのパラメータは Parameters タブから変更可能です(以前のバージョンではこれらのパラメータが cfg.py にハードコードされていました)。

キャッシングのパラメータはまだ cfg.py にハードコードされています。また cfg.py には VALIDATE_ 制御変数とデバッグ出力を有効にするためのパラメータ(DEBUG=1, DEBUG=2)、REQUEST 変数にリモート IP アドレスとホスト名を含ませるようにするための定義が書かれています。

ソースコード

最初のバージョンの mysqlUserFolder は etcUserFolder をベースに書かれました。

ソースコードはタブを字下げに用い、タブストップを4個のスペースに設定して書かれています。python ではタブが8個のスペースとして見えるため、ソースコードを変更する場合は注意が必要です。mysqlUserFolder.py は字下げのタブストップ8で書かれています。

cfg.py にはそのほかの設定オプションも書かれています。

ディレクトリ dtml/ には "manage" DTML ファイルが置かれています。
ディレクトリ dtml.user/ にはサンプルの "user" DTML スクリプトが置かれています。

ディレクトリ sql/ にはデータベースとテーブルを作成する SQL スクリプトが置かれています。

最終更新 2003-09-20 21:06:24

9月 2003
1 2 3 4 5 6 7
8 91011121314
15161718192021
22232425262728
2930     
8月
2003
 10月
2003

MySQL user Folder 0.9.1 のアーカイブとともに配布されている README の翻訳です。

XML-Image Letterimage

© 2003-2010, Yasushi Iwata