Package fiftyfive.wicket.shiro

Source Code of fiftyfive.wicket.shiro.ShiroWicketPlugin

/**
* Copyright 2014 55 Minutes (http://www.55minutes.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fiftyfive.wicket.shiro;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import fiftyfive.wicket.shiro.markup.LoginPage;
import fiftyfive.wicket.shiro.markup.LogoutPage;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.aop.AuthenticatedAnnotationHandler;
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
import org.apache.shiro.authz.aop.GuestAnnotationHandler;
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
import org.apache.shiro.authz.aop.RoleAnnotationHandler;
import org.apache.shiro.authz.aop.UserAnnotationHandler;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.MetaDataKey;
import org.apache.wicket.Page;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.Session;

import org.apache.wicket.authorization.Action;
import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;
import org.apache.wicket.authorization.UnauthorizedInstantiationException;

import org.apache.wicket.markup.html.pages.AccessDeniedPage;

import org.apache.wicket.protocol.http.WebApplication;

import org.apache.wicket.request.IRequestHandler;

import org.apache.wicket.request.component.IRequestableComponent;

import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
import org.apache.wicket.request.cycle.IRequestCycleListener;
import org.apache.wicket.request.cycle.RequestCycle;

import org.apache.wicket.request.flow.ResetResponseException;

import org.apache.wicket.request.handler.PageProvider;
import org.apache.wicket.request.handler.RenderPageRequestHandler;
import org.apache.wicket.request.handler.RenderPageRequestHandler.RedirectPolicy;

import org.apache.wicket.request.http.WebRequest;

import org.apache.wicket.request.mapper.MountedMapper;
import org.apache.wicket.request.mapper.mount.MountMapper;

import org.apache.wicket.settings.ISecuritySettings;

import org.apache.wicket.util.lang.Args;


/**
* Enhances Wicket to integrate closely with the Apache Shiro security
* framework. With the {@code ShiroWicketPlugin} installed in your Wicket
* application, you will gain the following features:
* <ul>
* <li>You can use all of Shiro's authorization annotations
*     (like
*     {@link org.apache.shiro.authz.annotation.RequiresAuthentication @RequiresAuthentication}
*     and
*     {@link org.apache.shiro.authz.annotation.RequiresPermissions @RequiresPermissions})
*     on Wicket Pages. The {@code ShiroWicketPlugin} will ensure that only
*     authorized users can access these pages, and will show an appropriate
*     error page or login page otherwise.
*     See {@link #isInstantiationAuthorized isInstantiationAuthorized()}.
* </li>
* <li>You can also use the same Shiro annotations on individual components,
*     like Links and Panels. The {@code ShiroWicketPlugin} will automatically
*     hide these components from unauthorized users.
*     See {@link #isActionAuthorized isActionAuthorized()}.
* </li>
* <li>You can access Shiro directly at any time in your Wicket code
*     by calling
*     {@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}.
*     This gives you access to the rich set of security operations on the
*     Shiro {@link org.apache.shiro.subject.Subject Subject} that represents
*     the current user.
* </li>
* <li>Any uncaught Shiro
*     {@link AuthorizationException AuthorizationExceptions}
*     will be handled gracefully by redirecting the user to the
*     login page or an unauthorized page (by default, the home page).
*     This allows you to implement
*     comprehensive security rules using Shiro at any tier of your
*     application and be confident that your UI will handle them
*     appropriately.
*     See {@link #onException onException()}.
* </li>
* </ul>
* <h2>Installation</h2>
* Before you can use the {@code ShiroWicketPlugin}, you must have Shiro
* properly added to your application's {@code web.xml} file. Refer to the
* <a href="package-summary.html">package summary</a> of this Javadoc
* for a brief tutorial.
* <h3>{@code Application.init()}</h3>
* Once Shiro itself is installed, adding {@code ShiroWicketPlugin} can be as
* simple as adding one line to your Wicket application {@code init()}:
* <pre class="example">
* public class MyApplication extends WebApplication
* {
*     &#064;Override
*     protected void init()
*     {
*         super.init();
*         new ShiroWicketPlugin().install(this);
*     }
* }</pre>
* Most developers will want to customize the login page.
* The more complex real-world installation is thus:
* <pre class="example">
* public class MyApplication extends WebApplication
* {
*     &#064;Override
*     protected void init()
*     {
*         super.init();
*         new ShiroWicketPlugin()
*             .mountLoginPage("login", MyLoginPage.class)
*             .install(this);
*     }
* }</pre>
*
* @author Matt Brictson
* @since 3.0
*/
public class ShiroWicketPlugin
    extends AbstractRequestCycleListener
    implements IAuthorizationStrategy,
               IUnauthorizedComponentInstantiationListener
{
    /**
     * The key that will be used to obtain a localized message
     * when access is denied due to the user be unauthorized;
     * for example, "you are not allowed to access that page".
     */
    public static final String UNAUTHORIZED_MESSAGE_KEY = "unauthorized";

    /**
     * The key that will be used to obtain a localized message
     * when access is denied due to the user be unauthenticated;
     * for example, "you need to be logged in to continue".
     */
    public static final String LOGIN_REQUIRED_MESSAGE_KEY = "loginRequired";

    /**
     * The key that will be used to obtain a localized message
     * when logout is performed; for example, "you have been logged out".
     */
    public static final String LOGGED_OUT_MESSAGE_KEY = "loggedOut";

    private static final MetaDataKey<AuthorizationException> EXCEPTION_KEY =
        new MetaDataKey<AuthorizationException>() {};
   
    private static final MetaDataKey<ShiroWicketPlugin> PLUGIN_KEY =
        new MetaDataKey<ShiroWicketPlugin>() {};

    private static final AuthorizingAnnotationHandler[] HANDLERS =
        new AuthorizingAnnotationHandler[] {
            new AuthenticatedAnnotationHandler(),
            new GuestAnnotationHandler(),
            new PermissionAnnotationHandler(),
            new RoleAnnotationHandler(),
            new UserAnnotationHandler()
        };

    /**
     * Returns the {@code ShiroWicketPlugin} instance that has been installed
     * in the current Wicket application. This is a convenience method that
     * only works within a Wicket thread, and it assumes that
     * {@link #install install()} has already been called.
     *
     * @throws IllegalStateException if there is no Wicket application bound
     *                               to the current thread, or if a
     *                               {@code ShiroWicketPlugin} has not been
     *                               installed.
     */
    public static ShiroWicketPlugin get()
    {
        Application app = Application.get();
        if(null == app)
        {
            throw new IllegalStateException(
                "No wicket application is bound to the current thread."
            );
        }
        ShiroWicketPlugin plugin = app.getMetaData(PLUGIN_KEY);
        if(null == plugin)
        {
            throw new IllegalStateException(
                "A ShiroWicketPlugin has not been installed in this Wicket " +
                "application. You must call ShiroWicketPlugin.install() in " +
                "your application init()."
            );
        }
        return plugin;
    }
   
    /**
     * Register the specified {@code ShiroWicketPlugin} in the application so that
     * {@link #get} will work. You should never need to call this method directly, unless you
     * are subclassing and overriding the {@link #install} method.
     */
    public static void set(Application app, ShiroWicketPlugin plugin)
    {
        app.setMetaData(PLUGIN_KEY, plugin);
    }
   
   
    private String loginPath = "login";
    private String logoutPath = "logout";
    private Class<? extends Page> loginPage = LoginPage.class;
    private Class<? extends Page> logoutPage = LogoutPage.class;
    private Class<? extends Page> unauthorizedPage = null;
    private boolean unauthorizedRedirect = true;
   
    /**
     * The login page class as provided to {@link #mountLoginPage}; the default is
     * {@link LoginPage}.
     */
    public Class<? extends Page> getLoginPage()
    {
        return loginPage;
    }

    /**
     * The logout page class as provided to {@link #mountLogoutPage}; the default is
     * {@link LogoutPage}.
     */
    public Class<? extends Page> getLogoutPage()
    {
        return logoutPage;
    }

    /**
     * Set the bookmarkable page that will be displayed when an <em>unauthenticated</em> user
     * attempts to access a page that requires authentication.
     *
     * @param mountPath The bookmarkable URI where the login page will be mounted when
     *                  {@link #install install} is called. The default is {@code "/login"}.
     *                  May be {@code null}, in which case the login page will not be mounted.
     *                  You would want to pass {@code null}, for example, if you use your home
     *                  page as the login page, since in that case the home page is already
     *                  implicitly mounted on {@code "/"}.
     *
     * @param loginPage The page to use as the login page when the user needs to be
     *                  authenticated. Cannot be {@code null}. The default is a simple
     *                  out-of-the-box {@link LoginPage}.
     *
     * @return {@code this} to allow chaining
     */
    public ShiroWicketPlugin mountLoginPage(String mountPath, Class<? extends Page> loginPage)
    {
        Args.notNull(loginPage, "loginPage");
        this.loginPath = mountPath;
        this.loginPage = loginPage;
        return this;
    }

    /**
     * Set the bookmarkable page that will be loaded to perform logout when the
     * {@link fiftyfive.wicket.shiro.markup.LogoutLink LogoutLink} is clicked.
     *
     * @param mountPath The bookmarkable URI where the logout page will be mounted when
     *                  {@link #install install} is called. The default is {@code "/logout"}.
     *
     * @param logoutPage The page to load when the user clicks the
     *                   {@link fiftyfive.wicket.shiro.markup.LogoutLink LogoutLink}. This page
     *                   is responsible for actually logging the user out of the Shiro system.
     *                   Cannot be {@code null}. The default is {@link LogoutPage},
     *                   which should be sufficient for most applications.
     *
     * @return {@code this} to allow chaining
     */
    public ShiroWicketPlugin mountLogoutPage(String mountPath, Class<? extends Page> logoutPage)
    {
        Args.notNull(logoutPage, "logoutPage");
        this.logoutPath = mountPath;
        this.logoutPage = logoutPage;
        return this;
    }
   
    /**
     * The page class that was set via {@link #setUnauthorizedPage}; otherwise the
     * application home page.
     */
    public Class<? extends Page> getUnauthorizedPage()
    {
        return unauthorizedPage != null ? unauthorizedPage : Application.get().getHomePage();
    }
   
    /**
     * The redirect flag that was set via {@link #setUnauthorizedPage}; {@code true} by default.
     */
    public boolean getUnauthorizedRedirect()
    {
        return unauthorizedRedirect;
    }
   
    /**
     * Set the bookmarkable page that will be displayed when an <em>authenticated</em> user
     * attempts to access a page that they are not allowed to see. By default it is
     * {@code null}, indicating that the application home page should be used.
     *
     * @param page The page to display when the user is unauthorized; if {@code null}, the
     *             application home page will be used
     *
     * @param redirect If {@code true}, a 302 redirect will be performed to display the page;
     *                 if {@code false}, no redirect will occur and the URL will not change.
     *                 The latter is appropriate for an error page.
     *
     * @return {@code this} to allow chaining
     */
    public ShiroWicketPlugin setUnauthorizedPage(Class<? extends Page> page, boolean redirect)
    {
        this.unauthorizedPage = page;
        this.unauthorizedRedirect = redirect;
        return this;
    }
   
    /**
     * The mount path for the login page as provided to {@link #mountLoginPage}; the default is
     * {@code "login"}.
     */
    public String getLoginPath()
    {
        return loginPath;
    }

    /**
     * The mount path for the logout action as provided to {@link #mountLogoutPage}; the default is
     * {@code "logout"}.
     */
    public String getLogoutPath()
    {
        return logoutPath;
    }

    /**
     * Installs this {@code ShiroWicketPlugin} by doing the following:
     * <ul>
     * <li>Sets itself as the {@link IAuthorizationStrategy}</li>
     * <li>And as the {@link IUnauthorizedComponentInstantiationListener}</li>
     * <li>And as an {@link IRequestCycleListener}</li>
     * <li>Mounts the login page</li>
     * <li>Mounts the logout page</li>
     * </ul>
     */
    public void install(WebApplication app)
    {
        Args.notNull(app, "app");
       
        ISecuritySettings settings = app.getSecuritySettings();
        settings.setAuthorizationStrategy(this);
        settings.setUnauthorizedComponentInstantiationListener(this);
        app.getRequestCycleListeners().add(this);
       
        // Mount bookmarkable URLs
        if(this.loginPath != null)
        {
            app.mount(new MountedMapper(this.loginPath, this.loginPage));
        }
        if(this.logoutPath != null)
        {
            app.mount(new MountedMapper(this.logoutPath, this.logoutPage));
        }
       
        // Install self in app metadata so that static get() can work
        ShiroWicketPlugin.set(app, this);
    }
   
    // Start feedback message callbacks --------------------------------------
   
    /**
     * Called by {@link LogoutPage} once the user has been logged out.
     * The default implementation adds a feedback message to the session that says
     * "you have been logged out". To override or localize this message,
     * define {@code loggedOut} in your application properties. You can disable the
     * message entirely by defining {@code loggedOut} as an empty string.
     */
    public void onLoggedOut()
    {
        String message = getLocalizedMessage(LOGGED_OUT_MESSAGE_KEY, "You have been logged out.");
        if(message != null && !message.matches("^\\s*$"))
        {
            // Invalidate current session and create a new one.
            // We need a new session because otherwise our feedback message won't "stick".
            Session session = Session.get();
            session.replaceSession();
       
            // Add localized "you have been logged out" message to session
            session.info(message);
        }
    }
   
    /**
     * Invoked by {@code ShiroWicketPlugin} when an anonymous or remembered user has tried to
     * access a page that requires authentication. The default implementation places a
     * "you need to be logged in to continue" feedback message in the session.
     * To override or localize this message,
     * define {@code loginRequired} in your application properties. You can disable the
     * message entirely by defining {@code loginRequired} as an empty string.
     */
    public void onLoginRequired()
    {
        String message = getLocalizedMessage(
            LOGIN_REQUIRED_MESSAGE_KEY,
            "You need to be logged in to continue.");
       
        if(message != null && !message.matches("^\\s*$"))
        {
            // We need a new session because otherwise our feedback message won't "stick".
            Session session = Session.get();
            session.bind();
       
            // Add localized "you have been logged out" message to session
            session.info(message);
        }
    }
   
    /**
     * Invoked by {@code ShiroWicketPlugin} when the user has tried to access a page
     * but lacks the necessary role or permission. The default implementation places a
     * "sorry, you are not allowed to access that page" feedback message in the session.
     * To override or localize this message,
     * define {@code unauthorized} in your application properties. You can disable the
     * message entirely by defining {@code unauthorized} as an empty string.
     */
    public void onUnauthorized()
    {
        String message = getLocalizedMessage(
            UNAUTHORIZED_MESSAGE_KEY,
            "Sorry, you are not allowed to access that page.");
       
        if(message != null && !message.matches("^\\s*$"))
        {
            // We need a new session because otherwise our feedback message won't "stick".
            Session session = Session.get();
            session.bind();
       
            // Add localized "sorry, you are not allowed to access that page" message to session
            session.error(message);
        }
    }
   
    // End feedback message callbacks ----------------------------------------
   
    // Start IRequestCycleListener methods -----------------------------------
   
    /**
     * React to an uncaught Exception by redirecting the browser to
     * the unauthorized page or login page if appropriate. This method will automatically be
     * called by Wicket if this plugin was installed by the standard {@link #install install()}
     * mechanism, via the {@link IRequestCycleListener} system.
     * This allows uncaught Shiro exceptions thrown by the backend to be
     * handled gracefully by the Wicket layer.
     * <p>
     * If the exception is a Shiro {@link AuthorizationException}, redirect
     * to the unauthorized page or login page depending on the type of error.
     * If the exception is not a Shiro {@link AuthorizationException}
     * return {@code null}.
     *
     * @param cycle The current request cycle, as provided by Wicket.
     *
     * @param error The exception to handle. If it is not a subclass of
     *              Shiro's {@link AuthorizationException}, this method will
     *              not have any effect.
     *
     * @return A {@link RenderPageRequestHandler} redirect to the login
     *         page if the error is due to the user being
     *         <em>unauthenticated</em>;
     *         {@link RenderPageRequestHandler} to render the unauthorized page
     *         if the error is due to the user being
     *         <em>unauthorized</em>.
     */
    @Override
    public IRequestHandler onException(RequestCycle cycle, Exception error)
    {
        Class<? extends Page> respondWithPage = null;
        RedirectPolicy redirectPolicy = RedirectPolicy.NEVER_REDIRECT;
       
        if(error instanceof AuthorizationException)
        {
            AuthorizationException ae = (AuthorizationException) error;
            if(authenticationNeeded(ae))
            {
                if(loginPage != null)
                {
                    onLoginRequired();
                   
                    // Create a RestartResponseAtInterceptPageException to set the intercept,
                    // even though we don't throw the exception. (The magic happens in the
                    // RestartResponseAtInterceptPageException constructor.)
                    new RestartResponseAtInterceptPageException(loginPage);
                    respondWithPage = loginPage;
                    redirectPolicy = RedirectPolicy.ALWAYS_REDIRECT;
                }
            }
            else
            {
                onUnauthorized();
               
                if(this.unauthorizedRedirect || (
                    cycle.getRequest() instanceof WebRequest &&
                    ((WebRequest) cycle.getRequest()).isAjax()))
                {
                    redirectPolicy = RedirectPolicy.ALWAYS_REDIRECT;
                }
                respondWithPage = getUnauthorizedPage();
            }
        }
        if(respondWithPage != null)
        {
            return new RenderPageRequestHandler(new PageProvider(respondWithPage), redirectPolicy);
        }
        return null;
    }

    // End IRequestCycleListener methods -------------------------------------

    // Start IUnauthorizedComponentInstantiationListener methods -------------

    /**
     * Determine what caused the unauthorized instantiation of the given
     * component. If access was denied due to being unauthenticated, and
     * the login page specified in the constructor was not {@code null},
     * call {@link #onLoginRequired} and redirect to the login page.
     * <p>
     * Otherwise, access was denied due to authorization failure (e.g. insufficient privileges),
     * call {@link #onUnauthorized} and render the unauthorized page (which is the home page by
     * default).
     *
     * @param component The component that failed to initialize due to
     *                  authorization or authentication failure
     *
     * @throws {@link ResetResponseException} to render the login page or unauthorized page
     *
     * @throws UnauthorizedInstantiationException the login page
     *                                            has not been configured (i.e. is {@code null})
     */
    public void onUnauthorizedInstantiation(Component component)
    {
        AuthorizationException cause;
        RequestCycle rc = RequestCycle.get();
        cause = rc.getMetaData(EXCEPTION_KEY);
       
        // Show appropriate login or error page if possible
        IRequestHandler handler = onException(rc, cause);
        if(handler != null)
        {
            throw new ResetResponseException(handler) {};
        }
       
        // Otherwise bubble up the error
        UnauthorizedInstantiationException ex;
        ex = new UnauthorizedInstantiationException(component.getClass());
        ex.initCause(cause);
        throw ex;
    }

    // End IUnauthorizedComponentInstantiationListener methods ---------------

    // Start IAuthorizationStrategy methods ----------------------------------
   
    /**
     * Performs authorization checks for the {@link Component#RENDER RENDER}
     * action only. Other actions are always allowed.
     * <p>
     * If the action is {@code RENDER}, the component class <em>and its
     * superclasses</em> are checked for the presence of
     * {@link org.apache.shiro.authz.annotation Shiro annotations}.
     * <p>
     * The absence of any Shiro annotation means that the component may be
     * rendered, and {@code true} is returned. Otherwise, each annotation is
     * evaluated against the current Shiro Subject. If any of the requirements
     * dictated by the annotations fail, {@code false} is returned and
     * rendering for the component will be skipped.
     * <p>
     * For example, this link will be hidden if the user is already
     * authenticated:
     * <pre class="example">
     * &#064;RequiresGuest
     * public class LoginLink extends StatelessLink
     * {
     *     ...
     * }</pre>
     */
    public boolean isActionAuthorized(Component component, Action action)
    {
        if(Component.RENDER.equals(action))
        {
            try
            {
                assertAuthorized(component.getClass());
            }
            catch(AuthorizationException ae)
            {
                return false;
            }
        }
        return true;
    }
   
    /**
     * If {@code componentClass} is a subclass of {@link Page},
     * return {@code true} or {@code false} based on evaluation of any
     * {@link org.apache.shiro.authz.annotation Shiro annotations}
     * that are present on the page class declaration, <em>plus any annotations
     * present on its superclasses</em>.
     * <p>
     * The absence of any Shiro annotation means that the page can always be
     * instantiated, meaning {@code true} will always be returned. Otherwise,
     * each annotation is evaluated against the current Shiro Subject. If any
     * of the requirements dictated by the annotations fail, {@code false} will
     * be returned and instantiation will be denied.
     * <p>
     * For example, this page may only be instantiated if the user has
     * explictly authenticated (i.e. not just "remembered" via cookie) and
     * additionally has the "admin" role:
     * <pre class="example">
     * &#064;RequiresAuthentication
     * &#064;RequiresRoles("admin")
     * public class TopSecretPage extends WebPage
     * {
     *     ...
     * }</pre>
     * If {@code componentClass} is not a subclass of Page, always return
     * {@code true}. Non-page components may always be instantiated; however
     * their rendering can be controlled via annotations. See
     * {@link #isActionAuthorized isActionAuthorized()}.
     */
    public <T extends IRequestableComponent> boolean isInstantiationAuthorized(
        Class<T> componentClass)
    {
        if(Page.class.isAssignableFrom(componentClass))
        {
            try
            {
                assertAuthorized(componentClass);
            }
            catch(AuthorizationException ae)
            {
                // Store exception for use later in the request by onUnauthorizedInstantiation()
                RequestCycle.get().setMetaData(EXCEPTION_KEY, ae);
                return false;
            }
        }
        return true;
    }

    // End IAuthorizationStrategy methods ------------------------------------
   
    /**
     * Looks up a localized string from the application properties.
     */
    protected String getLocalizedMessage(String key, String theDefault)
    {
        return Application.get().getResourceSettings().getLocalizer().getString(
            key,
            null,
            null,
            theDefault);
    }

    /**
     * Returns {@code true} if the reason the user was denied access is
     * because she needs to authenticate.
     */
    protected boolean authenticationNeeded(AuthorizationException cause)
    {
        // IMPLEMENTATION NOTE - A simple solution would be:
        //   return ! SecurityUtils.getSubject().isAuthenticated();
        // In other words, the user needs to authenticate if she is not
        // already logged in (this is how it is done in Wicket auth-roles).
        // However this does not take into account the subtle difference in
        // Shiro between "authenticated" and "remembered" states. To ensure the
        // correct behavior we have to inspect the actual exception to see what
        // action to take.
       
        boolean needLogin = false;
       
        // Check if Shiro blocked access due to authentication
        if(cause instanceof UnauthenticatedException)
        {
            needLogin = true;
           
            // But... there is a rare case where Shiro can throw an
            // UnauthenticatedException even when the user is already logged
            // in. If the user is logged in and the page was annotated with
            // @RequiresGuest, Shiro throws an UnauthenticatedException, which
            // which is very misleading. Our only way to detect this scenario
            // is to parse the exception message. Yes, this is a hack.
           
            String msg = cause.getMessage();
            String guestError = "Attempting to perform a guest-only operation.";
            if(msg != null && msg.startsWith(guestError))
            {
                needLogin = false;
            }
        }
        return needLogin;
    }

    /**
     * @throws AuthorizationException if the given class, or any of its
     *         superclasses, has a Shiro annotation that fails its
     *         authorization check.
     */
    private void assertAuthorized(final Class<?> cls)
        throws AuthorizationException
    {
        Collection<Annotation> annotations = findAnnotations(cls);
        for(Annotation annot : annotations)
        {
            for(AuthorizingAnnotationHandler h : HANDLERS)
            {
                h.assertAuthorized(annot);
            }
        }
    }
   
    /**
     * Returns all annotations present on the given class and all of its
     * superclasses.
     */
    private Collection<Annotation> findAnnotations(final Class<?> cls)
    {
        List<Annotation> annots = new ArrayList<Annotation>(5);
        Class<?> currClass = cls;
        while(currClass != null)
        {
            annots.addAll(Arrays.asList(currClass.getDeclaredAnnotations()));
            currClass = currClass.getSuperclass();
        }
        return annots;
    }
}
TOP

Related Classes of fiftyfive.wicket.shiro.ShiroWicketPlugin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.