アクセス関数のフック

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
tsukumoto's picture

はじめまして、初めて投稿させていただきます。

当方、社内向けサイトをDrupal6で構築しています。
コンテンツのアクセス権について、Drupal標準のアクセス権に加えて社内組織を意識したアクセス権設定を行っています。

本来ならば、
hook_access($op, $node, $account)
を使いたいところなのですが、なぜかフックしてくれません。
例えば、funcというモジュールを作って、func_access()関数を定義してもこの関数内に入ってくれないのです。
何か他に定義が必要なのでしょうか?

現在は、hook_nodeapi()内で社内アクセス権チェックを行い、不可だったら強引にアクセス拒否ページに飛ばす実装に
していますが、検索結果表示時や、viewの一覧などには制御が効かないため、困っています。

よろしくお願いします。

Comments

hook_accessを呼び出しているのは、node.mo

dokumori's picture

hook_accessを呼び出しているのは、node.moduleのnode_access()です。2028行目で

$access = module_invoke($module, 'access', $op, $node, $account);

という記述があり、特定のcontent typeのnode_accessのみを呼んでいます。そしてこれはnode_menu()内で、'node/%node'へのリクエストがあった際のaccess callbackに指定されています。
そのため、カスタムモジュール内でhook_access()を実装しても、このカスタムモジュールがcontent typeを定義していて hook_view等を実装していない限り無視されることになります。

コンテンツタイプまたはノードごとにアクセス権を設定したい場合、content accessというモジュールがありますよ。

コメントありがとうございます。

tsukumoto's picture

コメントありがとうございます。

なるほど、hook_access()の、hookはnodeタイプを指定すべきなのですね。
例えば、hook_nodeapi()の場合、hookにはモジュール名を指定しますが、hook_access()も同様だと考えていました;

func_nodeapi() は、定義さえすれば全ノードについての_nodeapi()をフックできますが、
func_access()を定義しても、func というnodeタイプが存在しないから何も起こらない。
ですね。

contntent_accessやnode_access?なども調べてみましたが、
これらはいずれもDrupal上のロールをベースに、設定を細分化するもののようです。

社内向けサイトでは、ユーザの所属している組織IDを見てアクセス可否判断を行いたいため、
ロールをベースにしたDrupalのアクセスチェックとは関係の無いチェックをしています。
そのため、そのチェック処理をあたかもuser_access()のように割り込ませて、フック関数内で独自処理により、
TRUE/FALSEを返却すれば、後はDrupalが表示/非表示してくれる。というのが希望なのです。

Drupal的には会社の組織ごとにロールを作成し、ユーザを入れていくという方法の方が合っているのだと思いますが、。
ロールの数が膨大になるし、組織の変更に追従するのは大変なので、完全に外付けロジックとしているのです。

現在、hook関数の一覧を見ています。
おそらく、hook_node_access()などあれば目的に合ってるんじゃないかと思いますが。。
hook_node_access_records()とかありますね、書くときだけでしょうか?
もうちょっと調べて、解決したり何かわかればまた書き込みます。

ありがとうございます。

hook_node_access_records()など、

kyk's picture

hook_node_access_records()など、こちらのもので対応することと思います。

組織を何に見立てるか?ということになるかと思いますが、

Taxonomyを組織に見立て、

http://drupal.org/project/taxonomy_access

や、グループを組織に見立て、

http://drupal.org/project/og

を利用することもできるかと思います。

hook_node_access_records()はレコ

tsukumoto's picture

hook_node_access_records()はレコードの”書き込み時”のみ有効のようでした;

新しい社内システムは既存システムの置き換えを前提としているので、
日本らしい?階層化された組織や”役職”なども認識しないといけないのです。(組織や役職は外部DBから取得します)
そのため、アクセス権のチェック自体は既存モジュールでカバーすることはあきらめて作りこんだ次第です。

Taxonomyやグループは、今作りこんだ仕組みとは別の観点で役に立つかも知れないと思います。
ロール、社内アクセス権以外の、任意の人が集まったグループ等。

現在のところ、hook_nodeapi()でできるところまでするのが最善だと思っています。。
検索時は、$opに"search result"が入ってくるので、その場合にアクセス権チェックにより不可判断したら$nodeの内容を見えなくする。
これにより、コンテンツを直接アクセスした場合と検索結果の表示については、少なくともアクセス権の無いユーザにはタイトルやコンテンツの内容を見せなくすることができる。しかし、viewsや他のモジュールの表示については個別に対応していかなければいけない。。

hook_node_grants()はどうでしょう: ht

dokumori's picture

hook_node_grants()はどうでしょう:
http://api.drupal.org/api/function/hook_node_grants/6

なにか

kyk's picture

何か複雑そうで、楽しそうですね 8)

一番シンプルそうな、tac_liteを見てみましたが、

hook_node_grantsで領域を定義しておいて、
hook_node_access_recordsで、ノード単位で領域を割り当てていく仕組みのような気がします。

別途DBが必要な場合はさらに、hook_db_rewrite_sqlを定義する

たぶん、複数のアクセス制限に対応するための仕組みでしょうか・・・
モジュールのインストールなどを行った際に、node_rebuildが呼ばれて、全ての権限テーブルを再構築しているようです

tsukumoto's picture

コメントありがとうございます。
当初、片っ端からアクセス関連っぽいhook関数を作ってみた時に、幸いにも?hook_node_grants()はひっかかってくれました。

しかしながら、この関数はノードをアクセスすることのできるユーザ/ロールを渡して、
TRUE/FALSEの判断はDrupal自身にゆだねる動きをするようです。
ユーザ/ロールの縛りがある以上、”とにかく追加モジュール内で判断させてくれ”というのは無理そうです。
例えば、あるノードの特定組織IDのアクセスを許可していた場合、やはり特定組織IDのロールを作成するか、
所属ユーザを片っ端からリストアップしないといけなさそうです。

。。ですが、もしかしたらこの仕組みで求めてる機能が実現できるかも?という気もしてきました。
デフォルト動作で全ユーザアクセス不許可にできるならば、
「基本は全員不許可なんだけれども、独自ルーチンで許可ユーザと判断した場合に限り、このhook_node_grants()でカレントユーザIDをセットする。」
という動作でいいのではないか??邪道かもしれませんが。。
ちょっと調べてみます。

あきらめました; hook_node_grants()のパ

tsukumoto's picture

あきらめました;

hook_node_grants()のパラメータは、$accountと$opなのでやろうとしたことはできませんでした。
理想は、
  hook_node_access($node, $op) で戻り値がTRUE/FALSEのフック関数なんですがありませんね。
hook_nodeapi()は近いけど戻り値を見てくれない。。

っということで、ノードアクセス制御のhookはあきらめて対症療法とすることにしました。
ノードを表示する局面は主に下記があると思います。他にあった場合は、、その時考えますかねえ。
・直接参照(?q=node/$nid)
 hook_nodeapiでつかまえて、アクセス権が無いと判断した場合は”別のページに飛ばす”。
・viewsモジュールで表示する一覧
 views/includes/views.inc 内の render() 内でアクセスチェック関数を呼び出して不許可の場合はデータを消す。
・ナビゲーション
 アクセス権対応のナビゲーションを作る。
 でも、ブックはなかなかわかりにくいですね;

特にviewsの中を直接触るなどできるだけ避けたいところですが、やむなしと思っています。
viewsにもhook関数は用意されているようですが、これまたコレダというものは見つけられていません;
hook_views_api()、hook_views_query_substitutions()、hook_views_data()あたりを試しましたが、
アクセス権チェックを行う必須条件のノードIDが取得できない;;

^^;

kyk's picture

あきらめました;

そうですか・・^^;
たぶん、viewsで行う場合は、フィルターをあたりを定義するしかないでしょうね

/views/handlers/views_handler_filter~

でもやはりノードで行ったほうがスマートかなと。
試していないけど、このような感じなのではないでしょうか

<?php
function sosiki_node_grants($account, $op) {
 
$grants[sosiki_user_to_sosiki($account->uid)] = TRUE;
  return
$grants;
}

function
sosiki_node_access_records($node) {
 
$grants = array();
 
$realms = array('soumu', 'kikaku', 'eigyou');
 
  foreach (
$realms as $realm) {
   
$grants[] = array(
     
'realm' => $realm,
     
'gid' => (sosiki_user_to_sosiki($node->uid) == $realm ? TRUE : FALSE),
     
'grant_view' => TRUE,
     
'grant_update' => TRUE,
     
'grant_delete' => TRUE,
     
'priority' => 0,
    );
  }
 
  return
$grants;
}

function
sosiki_user_to_sosiki($uid) {
 
}
?>

この場合、ユーザーと組織が1対1になってしまうので、複数の組織に権限がある場合は、もっとごちゃごちゃするかと思われます。

node_grantsで、ユーザー単位の領域へのアクセス権

node_access_recordsで、ノード単位で、どの領域へ属するか

tsukumoto's picture

コメントありがとうございます。
今までひとりで悶々としていたので、こんな細かい話にまでつきあって頂けるなんて感謝の気持ちでいっぱいです。

ご指摘の通り、node_grants()の場合、ユーザごとに組織と結びつけないといけないので大変そうですね。
それと、node_access_records()はなぜか”書き込み時のみ”つかまえられるようです。

現在の社内アクセス権の制御は下記のようなかんじで実装しています。
ノードごとに
  array(許可する組織,許可する組織,許可する組織...);
というテーブルを作って、アクセスしようとするユーザが条件に合致するかを調べて許可/不許可を判断する。
組織は階層構造になっているため、例えばあるユーザの組織は下記のようになります。
  soumu-keiri-sanka-madogiwa

ノードについているアクセス権は、こんなかんじです。
  array("soumu-ikka", "soumu-nika", "soumu-sanka-madogiwa", "eigyou");
そしてアクセス許可/不許可の判断は、ユーザの所属がこの指定の”配下”にあるか?で判断します。

そのため、ユーザがノードに対して持っている権限を固定で管理しようとすると、
  soumu, soumu-sanka, soumu-sanka-madogiwa
という個別のロールを定義して、全てにこのユーザを所属させないといけなくなってしまうのです。
(+node_accessモジュールが必要になりそうです)
これを全社員の全所属に対して行うととても大変なことになりそうであります。

handlerを用いれば、もしかするとソースの変更ではなくて追加で対処できるかも知れませんが、
もう、気力がございません;;;;

う~ん

kyk's picture

どうもやはりogで実現出来そうに思うのですが・・・

g.d.oの場合、基本パブリックコンテンツで、グループへの参加も、ユーザーが自由に行えますが、
グループの設定でプライベートコンテンツのみ、ユーザーも管理者のみがインバイトするという形で運用できます。

また、生成されるグループも、ただのノードですから、
インポート系で一撃で作ることも可能かなと思います
別途固有のIDが必要であれば、CCKでフィールド設ければ済むことですし。

また、ユーザーのインバイトもテキストエリアに名前の一覧いれてぽちっとすればインバイトできるので、
例えば社員名簿のDBに組織名も入っているなら、組織ごとに名前の一覧を出力するSQL書いて~で対応できるのではないでしょうか
 (複数のグループに権限ある方は、そういうクエリーを投げる

組織が、10か100によって手間はだいぶ違いますが
人が100か10000でも対して手間は変わらないように思えます

あまりに組織が多いようであれば、となると
組織情報とogを同期するようなモジュールを書かれたほうが、
ogに関連するモジュールなどいろいろあるので
後々便利かなと思いました

ogとはOrganic

tsukumoto's picture

ogとはOrganic Groupsなんですね、モジュールを見てみました。
その存在を知らなかったです;

Drupal的には(できれば既存)モジュール内でどうにかしたいですが、
組織数は階層構造も意識すると1000くらいにはなりそうです;

ユーザ管理サーバとogの同期を確保する仕組みと、
ノードごとにogをベースにしたアクセス制御の仕組みがあると対処できるかも知れません。
しかしながら、今回は既にアクセス権の設定とユーザデータの取得/判定ロジックを作りこんでいるので、
今回は大掛かりな変更は難しいところです。

このあたり日本の?組織構造とDrupalのアクセス権をうまく整合をとる定番の方法があれば、
使える局面は多いのではないかと思います。
ただ、多くの会社の場合既にユーザ情報DBやサーバを持っていると思うのでそのあたりの連結部分は
作りこみになってしまいそうですが。。

ありがとうございます。

ogとはOrganic

kyk's picture

ogとはOrganic Groupsなんですね、モジュールを見てみました。
その存在を知らなかったです;

失礼しました 8)
パス名で覚えると何かと都合がよいもので、いつもの癖でした

Drupal的には(できれば既存)モジュール内でどうにかしたいですが、
組織数は階層構造も意識すると1000くらいにはなりそうです;

1000となると手作業は絡めたくないですね・・
そうなると、やはりviewsのフィルターを書くしかないのかなと思われますが、
その場合、クエリーの結果ではなく、WHERE句を返すことになるので
1度のクエリーで結果が分かるようなデーター構造にする必要があるように思えます。

それはそれで大変そうです

このあたり日本の?組織構造とDrupalのアクセス権をうまく整合をとる定番の方法があれば、
使える局面は多いのではないかと思います。
ただ、多くの会社の場合既にユーザ情報DBやサーバを持っていると思うのでそのあたりの連結部分は
作りこみになってしまいそうですが。。

きっと、ユーザー情報DBにルールなんてないでしょうし、
そういう部分に雇用を残しておいてもよいかと ;p

tsukumoto's picture

そうなんですよ、実際にアクセスする人が何人いるかは別としてw対象ユーザ自体は結構な量になるので、
所属している組織の数も少なくはないのです。

追加であれ、viewsに手を入れる必要がある事を考えるとやはり、hook_node_access($node, $op)のような、
対象ノードに対するアクセス権チェックを行うためのhook関数があったらいいな~と思いますね。

今回はユーザ(姓/名)のような情報は全て外づけで外部のDBから入力しDrupal上には置かないようにしました。
これも、本来ならばDrupalのprofile等を使ってうまくDrupalに取り込むのが本筋だろうと思うのですが、
モジュールの読み込み、設定等を組み合わせないと動作しないため、あきらめたのです。

つまり、全て自製モジュールによる外付け機能にし、hookだけで対処すれば自製モジュールと他のモジュールの関係は
無いため、自製モジュールのつけはずしによる影響も少なくて済むというメリットを優先しました。
また、役職による権限設定などいかにも日本っぽい?条件を設定する必要がある場合など全て自製で無いと怖い
というのもありますね。

いろいろ教えて頂いたり調べたりして勉強になりました。
ありがとうございます。