読者です 読者をやめる 読者になる 読者になる

ParticipantProvider入門

buridoc

概要

ぶりで権限に関する処理を司っているのがParticipantProviderインタフェースです。


Participantとは一般には「参加者」と訳されます。まあ、ワークフローの「登場人物」ぐらいに思っておきましょう。
フローを記述するXPDLで言えば、スイムレーンのロールにあたります。簡単にはParticipantName=ロール名です。


このParticipantを扱うための各種メソッドを提供するのがParticipantProviderインタフェースです。

このインタフェースでは4つのメソッドを宣言しています。
これらは大きく分けて2つのグループに分かれます。

  • (A)ユーザ情報DTOとユーザIDの相互変換メソッド
    IdentityInfo getUserId(Object userData); // (A-1)
    Object getUserData(IdentityInfo appUserId); // (A-2)
  • (B)権限チェック用メソッド
    boolean hasAuthority(ParticipantContext context); // (B-1)
    List<IdentityInfo> getAuthorizedUserIds(ParticipantContext context); // (B-2)

順番に説明します。

(A)ユーザ情報DTOとユーザIDの相互変換メソッド

    IdentityInfo getUserId(Object userData); // (A-1)
    Object getUserData(IdentityInfo appUserId); // (A-2)

この2つはわかりやすいと思います。


ユーザ情報を格納するDTOはアプリごとに実装されます。例えば、あるアプリでは、ログイン名、メールアドレス、氏名、住所などのフィールドがあるかもしれません。

ぶり上ではアプリ側のユーザ情報はIdentityInfoクラスで統一して取り扱います。IdentityInfoクラスは数字型ID(Long)と文字列型ID(String)の対をフィールドに持ちます。数字型IDとはDB上のレコード通番で、文字列型IDとはログイン("guest")、と考えるとイメージしやすいと思います。

このユーザDTOとIdentityInfoを相互変換するためのメソッドが(A-1)と(A-2)です。


ほとんどのアプリでは、これらの実装は非常にシンプルになります。
例えば、

    public class UserDto {
        private long userId;
        private String loginName;
        // setter, getterは省略
    }
    public interface UserDao {
        public UserDto findById(long userId);
    }

というユーザ情報DTOと、DAOがある場合のメソッド実装は

public class ParticipantProviderImpl implements ParticipantProvider {
    
    private UserDao userDao;
    
    public Object getUserData(IdentityInfo appUserId) {
        UserDto userData = userDao.findById(appUserId.getIdNumber());
        return userData;
    }
    
    public IdentityInfo getUserId(Object userData) {
        UserDto userDto = (UserDto) userData;
        IdentityInfo userId = new IdentityInfo();
        userId.setIdNumber(userDto.getUserId());
        userId.setIdString(userDto.getLoginName());
        return userId;
    }
    
    //...以下省略
}    

となります。簡単でしょ?

(B)権限チェック用メソッド

    boolean hasAuthority(ParticipantContext context); // (B-1)
    List<IdentityInfo> getAuthorizedUserIds(ParticipantContext context); // (B-2)

こっちが権限処理の本命です。


この2つはそれぞれ以下の用途で使われます。

  • (B-1)=実行権限のチェック
  • (B-2)=参照権限のチェック


前者は、現在のユーザが対象データに対して何らかの操作を実行してよいかどうかを判定します。
呼び出されるタイミングは、対象データに対する操作の実行時です。実行する操作を決定する際のフィルタリングのために呼ばれます。
実装例として、後述のParticipantContext中のロール名(participantName)を取得して、現在のユーザがそのロールに対応しているかどうかで判定する、などが考えられます。


後者は、対象データに対してどのユーザに参照権限を与えるか決定します。
呼び出されるタイミングは、対象データのステータス遷移が行われたときです。遷移後のステータスに対する参照可能ユーザを決定するために呼ばれます。
実装例として、後述のParticipantContext中のロール名(participantName)を取得して、そのロールに対応したユーザやその同一組織のユーザ、また、より権限の強い上位組織のユーザなどをまとめて返す、などが考えられます。
また、ここで返すユーザ群に与えられるのはあくまで参照権限のみです。実行時には別途hasAuthority()による実行権限チェックをパスする必要がありますのでご注意ください。
逆に、参照権限がないユーザでもhasAuthority()がtrueであれば実行することができます。それがうれしいことはあまりないと思いますが、参照権限と実行権限はそのように独立して指定できるんだということで。


どちらも引数はParticipantContextです。
中身を見てみましょう。

public class ParticipantContext {
    
    // 対象データをフロー上に一番最初に投入したユーザID
    private IdentityInfo insertUserId;
    
    // 現在の実行ユーザのID情報
    private IdentityInfo userId;

    // 現在の実行ユーザ情報
    private Object userData;
    
    // 対象のデータ(=伝票)
    private Object data;

    // 対象データを実行するのに必要なロール名(=XPDLのスイムレーン名)
    private String participantName;
    
    // 対象データを実行するのに必要なParticipantの種別(通常は「ROLE」)
    private String participantType;

    // (おまけ?)現在のコンテキストで対象としているデータの開始アクティビティのロール名(=XPDLのスイムレーン名)
    // hasAuhtority()の場合はnull。
    private String startParticipantName;

    // (おまけ)XPDLの情報にアクセスすることができる
    private BuriExecProcess process

    // (おまけ)Baoの実行時情報などが入っているコンテキスト情報(実はMapのサブクラス)
    private BuriUserContext userContext;

    // ...getter,setterは省略
}

これらの情報を元に、hasAuthority(), getAuthorizedUserIds()で判断ロジックをくむわけです。これだけの情報があれば、権限の判断には十分かと思います。
(おまけ)な情報は説明しにくく使い方もちょっと難しいのですが、ほとんどのフローではそれ以外の情報で対応できるんじゃないでしょうか。

フラグのためだけに使う項目をデータに含めてしまうのは設計上あまりおすすめされないのですが、やろうと思えば対象データ中のフラグ項目の状態によって実行・参照権限を判断することも簡単にできますね。


[2007/04/28 修正]
ParticipantContextのインスタンス変数を見直しました。

  • 変数の追加
    • userData
    • startParticipantName
    • process
    • userContext
  • 変数名の変更
    • startUserId→insertUserId
    • currentUserId→userId