S2Buriでユーザ権限ごとにアクセス制御(2)
isUserInRole()はデータがフローを流れている間余り必要ないので使うようにしてません。
詳細はburi2.xpdlのBuriWorkFlowsUtilをみてみると・・・
id:makotan
勘違いしてburi2.diconを丁寧に調べていた私がやってきましたよ。buri2.xpdlなんですね...。orz
気を取り直して。
buri2.xpdlって設計資料なんですね。BuriEngineはどういうロジックでActivityを選択するのかっていう。
とか思ったらもしかして違う?これ自体、S2Buriの内部処理を制御する設定ファイルそのもの?
どうもそれっぽいですね。
というか面白いですね。こうやって使うことで複雑なif文をコードから撲滅するわけですか。なるほど。ちょっと見えてきた。単なる帳票系ワークフローエンジンってわけではなさそう。
で、本題。ユーザ権限のチェックです。
buri2.xpdlの「Providerが設定されてるとき」Activityで
#activityList = WorkFlowsUtil.getActivityListByParticipant(#activityList,#args.participant)
というOGNL式が設定されていましたがこれですかね?
このWorkFlowsUtilというコンポーネントは、buri2.diconに
<component name="WorkFlowsUtil" class="org.seasar.buri.xpdl.util.impl.WorkFlowsUtilImpl"> <aspect>BuriInterceptorChain</aspect> <property name="pathUtil">pathUtil</property> <!--<property name="activitySelectRule">wakanago.ActivitySelectRule</property>--> <!--<property name="activitySelectRule">BuriActivitySelectRule</property>--> <aspect pointcut="getActivity,getActivityTagSelectList" >UtilCache</aspect> </component> <component name="BuriActivitySelectRule" class="org.seasar.buri.xpdl.rule.impl.BuriActivitySelectRule"> <aspect>BuriInterceptorChain</aspect> </component>
と定義されています。
ActivitySelectRuleが両方コメントアウトされてる?って一瞬悩みましたが、Seasar2だから自動インジェクションされてるだけですね。しばらくJavaじゃない世界に行ってたのですっかりぼけてました。
まあとりあえず、WorkFlowsUtilImplの実装内容をみてみます。
public List getActivityListByParticipant(List activityList,BuriParticipant participant) { List result = new ArrayList(); if(activityList == null || activityList.size() == 0) { return result; } ActivityTagSelect tagSelect = (ActivityTagSelect)activityList.get(0); BuriPath fstPath = tagSelect.getBuriPath(); return participantUtil.findParticipant(fstPath,activityList,participant); }
ってことで、ParticipantUtilに続きます。
同じくburi2.diconに
<component name="ParticipantUtil" class="org.seasar.buri.xpdl.util.impl.ParticipantUtilImpl"> <aspect>BuriInterceptorChain</aspect> <property name="pathUtil">pathUtil</property> <aspect pointcut="getParticipantList,getParticipantFromName,getParticipant,getParticipantArray" >UtilCache</aspect> </component>
とあるのでこれが自動インジェクションされてるわけですね。
ParticipantUtilImplが↓です。
public List findParticipant(BuriPath buriPath,List activityList,BuriParticipant participant) { WorkFlowPackageTagSelect packageTagSelect = flowsUtil.getWorkFlowPackage(buriPath); List result = new ArrayList(); ParticipantProvider provider = packageTagSelect.getParticipantProvider(); Iterator ite = activityList.iterator(); int roleTypeID = 100; while(ite.hasNext()) { ActivityTagSelect activity = (ActivityTagSelect)ite.next(); roleTypeID = appendActivity(buriPath,result,activity,participant,provider,roleTypeID); } return result; } protected int appendActivity(BuriPath buriPath,List result,ActivityTagSelect activity,BuriParticipant participant,ParticipantProvider provider,int roleTypeID) { Participant xpdlParticipant = getParticipantFromName(buriPath,activity.getActivity().getPerformer()); if(xpdlParticipant==null) { throw new BuriNoParticipantException(buriPath,participant.getUserData().toString()); } if( canExecuteParticipant(xpdlParticipant,participant,provider) ) { int newRoleTypeID = convParticipantTypeToVal(xpdlParticipant.getParticipantType()); if(roleTypeID > newRoleTypeID) { result.clear(); roleTypeID = newRoleTypeID; } if(roleTypeID == newRoleTypeID) { result.add(activity); } } return roleTypeID; } protected int convParticipantTypeToVal(ParticipantType participantType) { Long val = (Long)convTypeToVal.get(participantType.getType()); return val.intValue(); } protected boolean canExecuteParticipant(Participant xpdlParticipant,BuriParticipant participant,ParticipantProvider provider) { String participantName = xpdlParticipant.getName(); return provider.isUserInRole(participant.getUserData(),participantName); }
ちょっと長いですが、canExecuteParticipant()の中に待望のParitipantProvider#isUserInRole()が見えます。
これが毎回呼ばれない!っていうので困ってるのですが、もうちょっとでわかりそう。
canExecuteParticipant()はappendActivity()から呼ばれます。
appendActivity()内では、XPDLで対象Activityに対応付けられたParticipant情報を取得して、それがnullじゃなければcanExecuteParticipant()を呼んでいます。テスト中のXPDLではRoleを設定してあるので、canExecuteParticipant()は呼ばれるはず。つまり、省略される可能性があるとしたらもっと上流。
appendActivity()はfindParticipant()から呼ばれます。
findParticipant()では発見済みのActivity一覧の要素ごとにループしながらappendActivity()を呼んでいます。テストコードでは最低でも1件のActivityは見つかっているので、少なくとも1度は呼ばれるはず。つまり、省略される可能性があるとしたらもっと上流。
ということで、WorkFlowsUtilImpl#getActivityListByParticipant()まで戻ります。
このメソッドの中ではActivityがnullまたは0件じゃなければ、findParticipant()を呼んでいます。テストコード(ry
んじゃあ、WorkFlowsUtilImpl#getActivityListByParticipant()を呼んでいるのは?
となると、buri2.xpdlに戻るわけです。
つまり、テストコードで初回登録時以外のActivityを実行するときは、実はburi2.xpdlの「Providerが設定されてるとき」Activityではないルートを通っている、ということになりそうです。
と、そこまで書いてなんとなくわかってきました。
紛らわしいので、buri2.xpdl上のActivityをActivity@buri2、テスト用XPDLのActivityをActivity@テストと書くことにします。
で、「データIDがあるとき」Activity@buri2からの分岐で、既にActivity@テストが1つに絞られていると「一つだけ」Activity@buri2にショートカットしてしまうのです。だから、ParticipantProvider#isUserInRole()が実行されないわけです。
ふう。やっとつながった。
で、どうしようかな、と。
Activity@テストが1つに絞られていても「Activityが準備終わり」Activity@buri2に遷移するように、Transitionをとっぱらってしまえばよさそうですね。
やってみます。buri2.xpdlで、以下の修正をします。
- 「PathにActivityがあるとき」の系列:
- 「データIDがあるとき」→「一つだけ」へのTransitionを削除します。
- 「データIDがあるとき」→「Activityが準備終わり」へのTransitionのConditionを削除します。
- 「PathにActivityがないとき」の系列:
- 「データIDがあるとき」→「一つだけ」へのTransitionを削除します。
- 「データIDがあるとき」→「Activityが準備終わり」へのTransitionのConditionを「#activityList.size() > 0」に変更します。
で、buildしてjarを作って、入れ替えて、実行すると…、
あ、できた。
全部のActivity@テストでisUserInRole()による権限チェックが入るようになりました。
つまり、こういうことでOkですか?
あ、何もjarファイル内のwakanago.diconとburi2.xpdlを入れ替えなくても、自分のプロジェクト内でのburi2.diconで差し替えてしまえばいいのか。
追記
自分のテスト用プロジェクトに、buri2.xpdlとwakanago.diconをコピーしてきて、buri2.diconからそのwakanago.diconをincludeして、wakanago.diconからburi2.xpdlを読み込むように設定したら、あっさりとできました。
この辺りの柔軟性の高さがDIコンテナの良いところ。すばらしい。