001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.internal.services;
014
015import org.apache.tapestry5.MetaDataConstants;
016import org.apache.tapestry5.http.services.Request;
017import org.apache.tapestry5.internal.InternalConstants;
018import org.apache.tapestry5.services.ComponentEventRequestParameters;
019import org.apache.tapestry5.services.ComponentRequestFilter;
020import org.apache.tapestry5.services.ComponentRequestHandler;
021import org.apache.tapestry5.services.MetaDataLocator;
022import org.apache.tapestry5.services.PageRenderRequestParameters;
023import org.apache.tapestry5.services.security.ClientWhitelist;
024
025import java.io.IOException;
026
027/**
028 * Ensures that a component event request does not reach a page annotated with
029 * {@link org.apache.tapestry5.annotations.WhitelistAccessOnly} from a client that is not on the
030 * {@linkplain ClientWhitelist whitelist}.
031 *
032 * A component event request identifies two pages: the active page (from the URL path) and the
033 * containing page (from the {@link InternalConstants#CONTAINER_PAGE_NAME} request parameter), whose
034 * component actually handles the event. Code from both pages runs for the request, so both are
035 * checked here.
036 *
037 * {@link ComponentEventLinkEncoderImpl} performs the same check while decoding the request, and is
038 * the gate that normally rejects these requests. This filter repeats it independently, so that any
039 * other route to the component event pipeline is covered as well.
040 *
041 * Page render requests are passed through: they are checked while decoding, and the page render
042 * dispatcher does not honour the "component not found" attribute used here to force a 404.
043 *
044 * @see org.apache.tapestry5.annotations.WhitelistAccessOnly
045 * @see MetaDataConstants#WHITELIST_ONLY_PAGE
046 * @since 5.9.2
047 */
048public class WhitelistOnlyPageFilter implements ComponentRequestFilter
049{
050    private final Request request;
051
052    private final MetaDataLocator metaDataLocator;
053
054    private final ClientWhitelist clientWhitelist;
055
056    public WhitelistOnlyPageFilter(Request request, MetaDataLocator metaDataLocator, ClientWhitelist clientWhitelist)
057    {
058        this.request = request;
059        this.metaDataLocator = metaDataLocator;
060        this.clientWhitelist = clientWhitelist;
061    }
062
063    @Override
064    public void handleComponentEvent(ComponentEventRequestParameters parameters, ComponentRequestHandler handler)
065            throws IOException
066    {
067        if (isWhitelistOnlyAndNotValid(parameters.getActivePageName())
068                || isWhitelistOnlyAndNotValid(parameters.getContainingPageName()))
069        {
070            // Same treatment as a request naming a component that does not exist: handling is
071            // aborted and the dispatcher turns the request into a 404, rather than reporting that
072            // the page exists but is protected.
073
074            request.setAttribute(InternalConstants.REFERENCED_COMPONENT_NOT_FOUND, true);
075
076            return;
077        }
078
079        handler.handleComponentEvent(parameters);
080    }
081
082    @Override
083    public void handlePageRender(PageRenderRequestParameters parameters, ComponentRequestHandler handler)
084            throws IOException
085    {
086        // Pass these through to the default handler.
087        handler.handlePageRender(parameters);
088    }
089
090    private boolean isWhitelistOnlyAndNotValid(String canonicalizedPageName)
091    {
092        return metaDataLocator.findMeta(MetaDataConstants.WHITELIST_ONLY_PAGE, canonicalizedPageName, boolean.class)
093                && !clientWhitelist.isClientRequestOnWhitelist();
094    }
095}