ログ日記

作業ログと日記とメモ

wicket-auth-rolesをカスタマイズする

wicket-auth-rolesは便利なんだけどもう少し痒いところに手が届かない感がある。

 wicket-auth-rolesを使った場合はページクラスに対して@AuthorizeInstantiationを使用すれば簡単に認証制御が可能なのですが

「全てのPageクラスに対してアノテーション付けるの('A`)マンドクセ

という人も多いかと思います。そういう場合は下記のような方法を検討するとよいかなと思います。

  • 親クラスで権限を設定する
  • パッケージレベルで権限を設定する
wicket-auth-rolesのまとめ - その2 - ひたすらプログラミング日記

ここに書いてあるように親クラスやパッケージレベルで指定すれば少しは設定が楽になるのだが、まだ不満が残る。
親クラスで権限を設定した場合、アプリを作っていってページ共通の処理を親クラスに追加した後に権限を変更しようとするとめんどくさいことになる気がする。権限に関係ない処理を親クラスに書かなければ良いのだが、多重継承が出来ないJavaでうまく整理する方法が思い付かない。
パッケージレベルで設定した場合は、権限が増えていったときに混乱する恐れがある。せっかくGmailのタグのように権限を設定できるのに、パッケージレベルで設定してしまうと権限構成がツリー構造になってしまう。



そこで権限設定を一カ所に集約する方法で書いてみる。


wicket-auth-rolesを使う場合はWebApplicationにAuthenticatedWebApplicationクラスを使うことになる。
このクラスのinit()メソッドは

        @Override
        protected void init()
        {
                super.init();

                // Set authorization strategy and unauthorized instantiation listener
                getSecuritySettings().setAuthorizationStrategy(new RoleAuthorizationStrategy(this));
                getSecuritySettings().setUnauthorizedComponentInstantiationListener(this);
        }

このようになっていて、AuthorizationStrategyにRoleAuthorizationStrategyを設定している。
このクラスはCompoundAuthorizationStrategyクラスを継承しているので、CompoundAuthorizationStrategy#addメソッドを使って任意のStrategyを追加することができる。




まずはロールを定義しているRole.java

public class Role {
    public static final String ADMIN = "admin";
    public static final String STAFF = "staff";

    public static final String MANAGER = "manager";


    public static final Roles MANAGER_ROLE = new Roles();
    static {
        MANAGER_ROLE.add(ADMIN);
        MANAGER_ROLE.add(MANAGER);
    }
}

とりあえずMANAGERを追加。



そして新たなStrategyのクラスAppAuthorizationStrategy.javaを作る。

import java.util.HashMap;
import java.util.Map;

import org.apache.wicket.Component;
import org.apache.wicket.authorization.Action;
import org.apache.wicket.authroles.authorization.strategies.role.AbstractRoleAuthorizationStrategy;
import org.apache.wicket.authroles.authorization.strategies.role.IRoleCheckingStrategy;
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.request.component.IRequestableComponent;

/**
 * 各ページの権限
 *
 * \begin{itemize}
 * \item 管理権限
 *   \begin{itemize}
 *   \item ManagerPage: マネージャー用
 *   \end{itemize}
 * \end{itemize}
 *
 * @spec Role
 */
public class AppAuthorizationStrategy extends AbstractRoleAuthorizationStrategy {
    private static final Map<Class<?>, Roles> ROLES = new HashMap<Class<?>, Roles>();
    static {
        ROLES.put(ManagerPage.class, Role.MANAGER_ROLE);
    }

    public AppAuthorizationStrategy(IRoleCheckingStrategy roleCheckingStrategy) {
        super(roleCheckingStrategy);
    }

    @Override
    public <T extends IRequestableComponent> boolean isInstantiationAuthorized(
            Class<T> componentClass) {
        if (ROLES.containsKey(componentClass))
            return hasAny(ROLES.get(componentClass));

        return true;
    }

    @Override
    public boolean isActionAuthorized(Component component, Action action) {
        if (action.getName().equals(Action.RENDER) && ROLES.containsKey(component.getClass()))
            return hasAny(ROLES.get(component.getClass()));

        // ページクラスの表示権限がない場合はそれに対するリンクも表示しない
        if (component instanceof BookmarkablePageLink){
            if (ROLES.containsKey(((BookmarkablePageLink<?>) component).getPageClass())){
                return hasAny(ROLES.get(((BookmarkablePageLink<?>) component).getPageClass()));
            }
        }

        return true;
    }
}

このクラスにページと権限を追加していくことによって、全Componentクラスの権限をこのクラスだけで管理できる。
ここではページクラスを直接書いているが、別にinterfaceでもクラス名のprefixでも色々応用できるはず。


これをWebApplicationで追加する。

public class WebApp extends AuthenticatedWebApplication {
    @Override
    protected void init(){
        super.init();

        ((RoleAuthorizationStrategy)getSecuritySettings().getAuthorizationStrategy()).add(new AppAuthorizationStrategy(this));


ここまで書いておいてアレだが…欲しかったのは権限一カ所集約クラスよりもPageクラスの権限を見てBookmarkablePageLinkの表示/非表示を切り替える機能だったかもしれない。
もう一度あとで書く。