001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.authroles.authentication;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.Page;
021import org.apache.wicket.RestartResponseAtInterceptPageException;
022import org.apache.wicket.Session;
023import org.apache.wicket.WicketRuntimeException;
024import org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;
025import org.apache.wicket.authorization.UnauthorizedInstantiationException;
026import org.apache.wicket.authroles.authorization.strategies.role.IRoleCheckingStrategy;
027import org.apache.wicket.authroles.authorization.strategies.role.RoleAuthorizationStrategy;
028import org.apache.wicket.authroles.authorization.strategies.role.Roles;
029import org.apache.wicket.markup.html.WebPage;
030import org.apache.wicket.protocol.http.WebApplication;
031import org.apache.wicket.request.Request;
032import org.apache.wicket.request.Response;
033
034
035/**
036 * A web application subclass that does role-based authentication.
037 * 
038 * @author Jonathan Locke
039 */
040public abstract class AuthenticatedWebApplication extends WebApplication
041        implements
042                IRoleCheckingStrategy,
043                IUnauthorizedComponentInstantiationListener
044{
045        @Override
046        protected void init()
047        {
048                super.init();
049
050                // Set authorization strategy and unauthorized instantiation listener
051                getSecuritySettings().setAuthorizationStrategy(new RoleAuthorizationStrategy(this));
052                getSecuritySettings().setUnauthorizedComponentInstantiationListener(this);
053        }
054
055        @Override
056        public final boolean hasAnyRole(final Roles roles)
057        {
058                final Roles sessionRoles = AbstractAuthenticatedWebSession.get().getRoles();
059                return (sessionRoles != null) && sessionRoles.hasAnyRole(roles);
060        }
061
062        @Override
063        public final void onUnauthorizedInstantiation(final Component component)
064        {
065                // If there is a sign in page class declared, and the unauthorized
066                // component is a page, but it's not the sign in page
067                if (component instanceof Page)
068                {
069                        if (!AbstractAuthenticatedWebSession.get().isSignedIn())
070                        {
071                                // Redirect to intercept page to let the user sign in
072                                restartResponseAtSignInPage();
073                        }
074                        else
075                        {
076                                onUnauthorizedPage(component);
077                        }
078                }
079                else
080                {
081                        // The component was not a page, so throw an exception
082                        throw new UnauthorizedInstantiationException(component.getClass());
083                }
084        }
085
086        /**
087         * Restarts response at sign in page.
088         * 
089         * NOTE: this method internally throws a restart response exception, so no code after a call to
090         * this method will be executed
091         */
092        public void restartResponseAtSignInPage()
093        {
094                throw new RestartResponseAtInterceptPageException(getSignInPageClass());
095        }
096
097        /**
098         * {@inheritDoc}
099         */
100        @Override
101        public Session newSession(final Request request, final Response response)
102        {
103                Class<? extends AbstractAuthenticatedWebSession> webSessionClass = getWebSessionClass();
104                try
105                {
106                        return webSessionClass
107                                .getDeclaredConstructor(Request.class)
108                                .newInstance(request);
109                }
110                catch (Exception e)
111                {
112                        throw new WicketRuntimeException("Unable to instantiate web session " +
113                                webSessionClass, e);
114                }
115        }
116
117        /**
118         * @return BaseAuthenticatedWebSession subclass to use in this authenticated web application.
119         */
120        protected abstract Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass();
121
122        /**
123         * @return Subclass of sign-in page
124         */
125        protected abstract Class<? extends WebPage> getSignInPageClass();
126
127        /**
128         * Called when an AUTHENTICATED user tries to navigate to a page that they are not authorized to
129         * access. You might want to override this to navigate to some explanatory page or to the
130         * application's home page.
131         * 
132         * @param page
133         *            The partially constructed page (only the component id is guaranteed to be valid).
134         */
135        protected void onUnauthorizedPage(final Component page)
136        {
137                throw new UnauthorizedInstantiationException(page.getClass());
138        }
139}