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.csp;
018
019import org.apache.wicket.request.IRequestHandler;
020import org.apache.wicket.request.IRequestHandlerDelegate;
021import org.apache.wicket.request.cycle.IRequestCycleListener;
022import org.apache.wicket.request.cycle.RequestCycle;
023import org.apache.wicket.request.http.WebResponse;
024
025/**
026 * An {@link IRequestCycleListener} that adds {@code Content-Security-Policy} and/or
027 * {@code Content-Security-Policy-Report-Only} headers based on the supplied configuration.
028 *
029 * @author Sven Haster
030 * @author Emond Papegaaij
031 */
032public class CSPRequestCycleListener implements IRequestCycleListener
033{
034        private final ContentSecurityPolicySettings settings;
035
036        public CSPRequestCycleListener(ContentSecurityPolicySettings settings)
037        {
038                this.settings = settings;
039        }
040
041        @Override
042        public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
043        {
044                // WICKET-7028- this is needed for redirect to buffer use case.
045                protect(cycle, handler);
046        }
047
048        @Override
049        public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler)
050        {
051                protect(cycle, handler);
052        }
053
054        protected void protect(RequestCycle cycle, IRequestHandler handler)
055        {
056                if (!mustProtect(handler) || !(cycle.getResponse() instanceof WebResponse))
057                {
058                        return;
059                }
060
061                WebResponse webResponse = (WebResponse)cycle.getResponse();
062                if (!webResponse.isHeaderSupported())
063                {
064                        return;
065                }
066
067                settings.getConfiguration().entrySet().stream().filter(entry -> entry.getValue().isSet())
068                                .forEach(entry -> {
069                                        CSPHeaderMode mode = entry.getKey();
070                                        CSPHeaderConfiguration config = entry.getValue();
071                                        String headerValue = config.renderHeaderValue(settings, cycle);
072                                        webResponse.setHeader(mode.getHeader(), headerValue);
073                                        if (config.isAddLegacyHeaders())
074                                        {
075                                                webResponse.setHeader(mode.getLegacyHeader(), headerValue);
076                                        }
077                                });
078        }
079
080        /**
081         * Must the given handler be protected.
082         * 
083         * @param handler
084         *            handler
085         * @return <code>true</code> if must be protected
086         * @see ContentSecurityPolicySettings#mustProtectRequest(IRequestHandler)
087         */
088        protected boolean mustProtect(IRequestHandler handler)
089        {
090                if (handler instanceof IRequestHandlerDelegate)
091                {
092                        return mustProtect(((IRequestHandlerDelegate)handler).getDelegateHandler());
093                }
094                
095                return settings.mustProtectRequest(handler);
096        }
097
098}