You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2020/08/27 11:59:34 UTC
[wicket] branch master updated: [WICKET-6805] Add Cross-Origin
Opener Policy and Cross-Origin Embedder Policy support (#442)
This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git
The following commit(s) were added to refs/heads/master by this push:
new 0f42d33 [WICKET-6805] Add Cross-Origin Opener Policy and Cross-Origin Embedder Policy support (#442)
0f42d33 is described below
commit 0f42d3314f1200a3b88f76e435910ef389af6b58
Author: Ecenaz Ozmen <eo...@columbia.edu>
AuthorDate: Thu Aug 27 14:59:26 2020 +0300
[WICKET-6805] Add Cross-Origin Opener Policy and Cross-Origin Embedder Policy support (#442)
* COOP and COEP Implementation (#5)
* Initial coop implementation
* Fixed typo +reformatting code
* Update wicket-core/src/main/java/org/apache/wicket/coop/CoopConfiguration.java
Co-authored-by: Sal <sa...@gmail.com>
* Update wicket-core/src/main/java/org/apache/wicket/coop/CoopConfiguration.java
Co-authored-by: Sal <sa...@gmail.com>
* Updates based on comments on the PR
* Initial COEP implementation that doesn't handle report-to and setting up a reporting endpoint
* Added javadocs and reformatted code
* Fixed typo in javadoc
* Updated valid values for COOP, same-origin-allow-popups instead of same-site
* Made builder methods public so they can be called from init() in a sample app, added default values for builder fields to avoid null pointer exceptions
* making exempted paths a HashSet for faster lookup
* Using Set instead of HashSet in the declaration of exemptedPaths + reformatting code
* Reformatting code to match Wicket's style
* Indentation fix for CoepMode enum
* Added tests for each COOP value, inlined url argument for checkHeaders in tests, formatted log statement to include path variable for exempted paths
Co-authored-by: Sal <sa...@gmail.com>
* Fixed typo in COEP debug statement
* Refactoring configs into SecuritySettings for COOP and COEP
* Renamed the request cycle listeners and tests with longer names of coop and coep, removed CoopConfiguration and CoepConfiguration files
* Renamed securityInit() method to coopCoepInit()
* Moved adding coop/coep listeners to validetInit in WebApplication
* WICKET-6805 Formatting, cleanup and minor improvements
Co-authored-by: Sal <sa...@gmail.com>
Co-authored-by: Martin Tzvetanov Grigorov <mg...@apache.org>
---
.../CrossOriginEmbedderPolicyConfiguration.java | 109 ++++++++++++++++++
...ssOriginEmbedderPolicyRequestCycleListener.java | 94 ++++++++++++++++
.../coop/CrossOriginOpenerPolicyConfiguration.java | 113 +++++++++++++++++++
...rossOriginOpenerPolicyRequestCycleListener.java | 90 +++++++++++++++
.../wicket/protocol/http/WebApplication.java | 27 +++++
.../apache/wicket/settings/SecuritySettings.java | 66 ++++++++++-
...iginEmbedderPolicyRequestCycleListenerTest.java | 113 +++++++++++++++++++
...OriginOpenerPolicyRequestCycleListenerTest.java | 122 +++++++++++++++++++++
8 files changed, 733 insertions(+), 1 deletion(-)
diff --git a/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyConfiguration.java b/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyConfiguration.java
new file mode 100644
index 0000000..4094b74
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyConfiguration.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coep;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.util.lang.Args;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Specifies the configuration for Cross-Origin Embedder Policy to be used for
+ * {@link CrossOriginEmbedderPolicyRequestCycleListener}. Users can specify the paths that should be exempt from COEP and
+ * one of 3 modes (<code>REPORTING, ENFORCING, DISABLED</code>) for the policy. The config object
+ * lives in {@link org.apache.wicket.settings.SecuritySettings}, users can specify their COOP
+ * preferences with the following lines in their application's {@link WebApplication#init()} method:
+ *
+ * <pre>
+ * @Override
+ * protected void init()
+ * {
+ * // ...
+ * getSecuritySettings().setCrossOriginEmbedderPolicyConfiguration(CoepMode.REPORTING,
+ * "EXEMPTED PATHS");
+ * // ...
+ * }
+ * </pre>
+ *
+ * The config value will be read once at startup in {@link Application#initApplication()}, changing
+ * the configuration at runtime will have no effect of the COOP headers set.
+ *
+ * @author Santiago Diaz - saldiaz@google.com
+ * @author Ecenaz Jen Ozmen - ecenazo@google.com
+ *
+ * @see CrossOriginEmbedderPolicyRequestCycleListener
+ * @see org.apache.wicket.settings.SecuritySettings
+ */
+public class CrossOriginEmbedderPolicyConfiguration
+{
+ public enum CoepMode
+ {
+ ENFORCING("Cross-Origin-Embedder-Policy"),
+ REPORTING("Cross-Origin-Embedder-Policy-Report-Only"),
+ DISABLED("");
+
+ final String header;
+
+ CoepMode(String header)
+ {
+ this.header = header;
+ }
+ }
+
+ private final Set<String> exemptions = new HashSet<>();
+ private final CoepMode mode;
+
+ public CrossOriginEmbedderPolicyConfiguration(CoepMode mode, String... exemptions)
+ {
+ this.exemptions.addAll(Arrays.asList(exemptions));
+ this.mode = Args.notNull(mode, "mode");
+ }
+
+ public CrossOriginEmbedderPolicyConfiguration(CoepMode mode)
+ {
+ this.mode = Args.notNull(mode, "mode");
+ }
+
+ public Set<String> getExemptions()
+ {
+ return exemptions;
+ }
+
+ public CoepMode getMode()
+ {
+ return mode;
+ }
+
+ public String getCoepHeader()
+ {
+ return mode.header;
+ }
+
+ public CrossOriginEmbedderPolicyConfiguration addExemptedPath(String path)
+ {
+ exemptions.add(path);
+ return this;
+ }
+
+ public boolean isEnabled()
+ {
+ return mode != CoepMode.DISABLED;
+ }
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListener.java
new file mode 100644
index 0000000..d2ecfa0
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListener.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coep;
+
+import org.apache.wicket.coop.CrossOriginOpenerPolicyRequestCycleListener;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.util.lang.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Sets <a href="https://wicg.github.io/cross-origin-embedder-policy/">Cross-Origin Embedder
+ * Policy</a> (COEP) headers on the responses based on the mode specified by
+ * {@link CrossOriginEmbedderPolicyConfiguration}. COEP can be enabled in <code>REPORTING</code>
+ * mode which will set the headers as <code>Cross-Origin-Embedder-Policy-Report-Only</code> or
+ * <code>ENFORCING</code> mode which will set the header as
+ * <code>Cross-Origin-Embedder-Policy</code>. The header is not set for the paths that are exempted
+ * from COEP. The only valid value of COEP is <code>require-corp</code>, so if the listener is
+ * enabled the policy value will be specified as so.
+ *
+ * COEP prevents a document from loading any non-same-origin resources which don't explicitly grant
+ * the document permission to be loaded. Using COEP and COOP together allows developers to safely
+ * use powerful features such as <code>SharedArrayBuffer</code>,
+ * <code>performance.measureMemory()</code>, and the JS Self-Profiling API.See
+ * {@link CrossOriginOpenerPolicyRequestCycleListener} for instructions on how to enable COOP.
+ * Read more about cross-origin isolation on
+ * <a href="https://web.dev/why-coop-coep/">https://web.dev/why-coop-coep/</a>
+ *
+ *
+ * @author Santiago Diaz - saldiaz@google.com
+ * @author Ecenaz Jen Ozmen - ecenazo@google.com
+ *
+ * @see CrossOriginEmbedderPolicyConfiguration
+ * @see org.apache.wicket.settings.SecuritySettings
+ */
+public class CrossOriginEmbedderPolicyRequestCycleListener implements IRequestCycleListener
+{
+ private static final Logger log = LoggerFactory.getLogger(CrossOriginEmbedderPolicyRequestCycleListener.class);
+
+ static final String REQUIRE_CORP = "require-corp";
+
+ private CrossOriginEmbedderPolicyConfiguration coepConfig;
+
+ public CrossOriginEmbedderPolicyRequestCycleListener(CrossOriginEmbedderPolicyConfiguration coepConfig)
+ {
+ this.coepConfig = Args.notNull(coepConfig, "coepConfig");
+ }
+
+ @Override
+ public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
+ {
+ final Object containerRequest = cycle.getRequest().getContainerRequest();
+ if (containerRequest instanceof HttpServletRequest)
+ {
+ HttpServletRequest request = (HttpServletRequest) containerRequest;
+ String path = request.getContextPath();
+ final String coepHeaderName = coepConfig.getCoepHeader();
+
+ if (coepConfig.getExemptions().contains(path))
+ {
+ log.debug("Request path {} is exempted from COEP, no '{}' header added", path, coepHeaderName);
+ return;
+ }
+
+ if (cycle.getResponse() instanceof WebResponse)
+ {
+ WebResponse webResponse = (WebResponse) cycle.getResponse();
+ if (webResponse.isHeaderSupported())
+ {
+ webResponse.setHeader(coepHeaderName, REQUIRE_CORP);
+ }
+ }
+ }
+ }
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyConfiguration.java b/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyConfiguration.java
new file mode 100644
index 0000000..ff363bd
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyConfiguration.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coop;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.util.lang.Args;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Specifies the configuration for Cross-Origin Opener Policy to be used by
+ * {@link CrossOriginOpenerPolicyRequestCycleListener} when adding COOP headers. Users can specify the paths that
+ * should be exempt from COOP and one of 4 modes
+ * (<code>UNSAFE_NONE, SAME_ORIGIN, SAME_ORIGIN_ALLOW_POPUPS, DISABLED</code>) for the policy. The
+ * config object lives in {@link org.apache.wicket.settings.SecuritySettings}, users can specify
+ * their COOP preferences with the following lines in their application's
+ * {@link WebApplication#init()} method:
+ *
+ * <pre>
+ * @Override
+ * protected void init()
+ * {
+ * // ...
+ * getSecuritySettings().setCrossOriginOpenerPolicyConfiguration(CoopMode.SAME_ORIGIN,
+ * "EXEMPTED PATHS");
+ * // ...
+ * }
+ * </pre>
+ *
+ * The config value will be read once at startup in {@link Application#initApplication()}, changing
+ * the configuration at runtime will have no effect of the COOP headers set.
+ *
+ * @author Santiago Diaz - saldiaz@google.com
+ * @author Ecenaz Jen Ozmen - ecenazo@google.com
+ *
+ * @see CrossOriginOpenerPolicyRequestCycleListener
+ * @see org.apache.wicket.settings.SecuritySettings
+ */
+public class CrossOriginOpenerPolicyConfiguration
+{
+ public enum CoopMode
+ {
+ UNSAFE_NONE("unsafe-none"),
+ SAME_ORIGIN("same-origin"),
+ SAME_ORIGIN_ALLOW_POPUPS("same-origin-allow-popups"),
+ DISABLED("");
+
+ final String keyword;
+
+ CoopMode(String keyword)
+ {
+ this.keyword = keyword;
+ }
+ }
+
+
+ private final Set<String> exemptions = new HashSet<>();
+ private final CoopMode mode;
+
+ public CrossOriginOpenerPolicyConfiguration(CoopMode mode, String... exemptions)
+ {
+ this.exemptions.addAll(Arrays.asList(exemptions));
+ this.mode = Args.notNull(mode, "mode");
+ }
+
+ public CrossOriginOpenerPolicyConfiguration(CoopMode mode)
+ {
+ this.mode = Args.notNull(mode, "mode");
+ }
+
+ public CrossOriginOpenerPolicyConfiguration addExemptedPath(String path)
+ {
+ exemptions.add(path);
+ return this;
+ }
+
+ public Set<String> getExemptions()
+ {
+ return exemptions;
+ }
+
+ public CoopMode getMode()
+ {
+ return mode;
+ }
+
+ public String getHeaderValue()
+ {
+ return mode.keyword;
+ }
+
+ public boolean isEnabled()
+ {
+ return mode != CoopMode.DISABLED;
+ }
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListener.java
new file mode 100644
index 0000000..7474796
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListener.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coop;
+
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyRequestCycleListener;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.util.lang.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Sets <a href="https://github.com/whatwg/html/pull/5334/files">Cross-Origin Opener Policy</a>
+ * headers on the responses based on the policy specified by {@link CrossOriginOpenerPolicyConfiguration}. The header
+ * is not set for the paths that are exempted from COOP.
+ *
+ * COOP is a mitigation against cross-origin information leaks and is used to make websites
+ * cross-origin isolated. Setting the COOP header allows you to ensure that a top-level window is
+ * isolated from other documents by putting them in a different browsing context group, so they
+ * cannot directly interact with the top-level window. Using COEP and COOP together allows
+ * developers to safely use * powerful features such as <code>SharedArrayBuffer</code>,
+ * <code>performance.measureMemory()</code>, * and the JS Self-Profiling API.See
+ * {@link CrossOriginEmbedderPolicyRequestCycleListener} for instructions * on how to enable COOP.
+ * Read more about cross-origin isolation on
+ * <a href="https://web.dev/why-coop-coep/">https://web.dev/why-coop-coep/</a>
+ *
+ *
+ * @author Santiago Diaz - saldiaz@google.com
+ * @author Ecenaz Jen Ozmen - ecenazo@google.com
+ *
+ * @see CrossOriginOpenerPolicyConfiguration
+ * @see org.apache.wicket.settings.SecuritySettings
+ */
+public class CrossOriginOpenerPolicyRequestCycleListener implements IRequestCycleListener
+{
+ private static final Logger log = LoggerFactory.getLogger(CrossOriginOpenerPolicyRequestCycleListener.class);
+
+ static final String COOP_HEADER = "Cross-Origin-Opener-Policy";
+
+ private CrossOriginOpenerPolicyConfiguration coopConfig;
+
+ public CrossOriginOpenerPolicyRequestCycleListener(CrossOriginOpenerPolicyConfiguration coopConfig)
+ {
+ this.coopConfig = Args.notNull(coopConfig, "coopConfig");
+ }
+
+ @Override
+ public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
+ {
+ final Object containerRequest = cycle.getRequest().getContainerRequest();
+ if (containerRequest instanceof HttpServletRequest)
+ {
+ HttpServletRequest request = (HttpServletRequest) containerRequest;
+ String path = request.getContextPath();
+
+ if (coopConfig.getExemptions().contains(path))
+ {
+ log.debug("Request path {} is exempted from COOP, no {} header added", path, COOP_HEADER);
+ return;
+ }
+
+ if (cycle.getResponse() instanceof WebResponse)
+ {
+ WebResponse webResponse = (WebResponse) cycle.getResponse();
+ if (webResponse.isHeaderSupported())
+ {
+ webResponse.setHeader(COOP_HEADER, coopConfig.getHeaderValue());
+ }
+ }
+ }
+ }
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
index c682e715..22dcc71 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
@@ -37,6 +37,10 @@ import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AjaxRequestHandler;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.AjaxRequestTargetListenerCollection;
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyConfiguration;
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyRequestCycleListener;
+import org.apache.wicket.coop.CrossOriginOpenerPolicyConfiguration;
+import org.apache.wicket.coop.CrossOriginOpenerPolicyRequestCycleListener;
import org.apache.wicket.core.request.mapper.IMapperContext;
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.core.request.mapper.PackageMapper;
@@ -773,6 +777,29 @@ public abstract class WebApplication extends Application
getCspSettings().blocking().strict();
}
+ @Override
+ protected void validateInit()
+ {
+ super.validateInit();
+
+ // enable coop and coep listeners if specified in security settings
+ CrossOriginOpenerPolicyConfiguration coopConfig = getSecuritySettings()
+ .getCrossOriginOpenerPolicyConfiguration();
+ if (coopConfig.isEnabled())
+ {
+ getRequestCycleListeners()
+ .add(new CrossOriginOpenerPolicyRequestCycleListener(coopConfig));
+ }
+
+ CrossOriginEmbedderPolicyConfiguration coepConfig = getSecuritySettings()
+ .getCrossOriginEmbedderPolicyConfiguration();
+ if (coepConfig.isEnabled())
+ {
+ getRequestCycleListeners()
+ .add(new CrossOriginEmbedderPolicyRequestCycleListener(coepConfig));
+ }
+ }
+
/**
* set runtime configuration type
* <p/>
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
index 9556f98..a618ee2 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
@@ -24,6 +24,10 @@ import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;
import org.apache.wicket.authorization.IUnauthorizedResourceRequestListener;
import org.apache.wicket.authorization.UnauthorizedInstantiationException;
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyConfiguration;
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyConfiguration.CoepMode;
+import org.apache.wicket.coop.CrossOriginOpenerPolicyConfiguration;
+import org.apache.wicket.coop.CrossOriginOpenerPolicyConfiguration.CoopMode;
import org.apache.wicket.core.random.DefaultSecureRandomSupplier;
import org.apache.wicket.core.random.ISecureRandomSupplier;
import org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory;
@@ -57,7 +61,7 @@ public class SecuritySettings
/** factory for creating crypt objects */
private ICryptFactory cryptFactory;
-
+
/** supplier of random data and SecureRandom */
private ISecureRandomSupplier randomSupplier = new DefaultSecureRandomSupplier();
@@ -69,6 +73,18 @@ public class SecuritySettings
*/
private boolean enforceMounts = false;
+ /**
+ * Represents the configuration for Cross-Origin-Opener-Policy headers
+ */
+ private CrossOriginOpenerPolicyConfiguration crossOriginOpenerPolicyConfiguration = new CrossOriginOpenerPolicyConfiguration(
+ CoopMode.SAME_ORIGIN);
+
+ /**
+ * Represents the configuration for Cross-Origin-Embedder-Policy headers
+ */
+ private CrossOriginEmbedderPolicyConfiguration crossOriginEmbedderPolicyConfiguration = new CrossOriginEmbedderPolicyConfiguration(
+ CoepMode.REPORTING);
+
/** Authorizer for component instantiations */
private static final IUnauthorizedComponentInstantiationListener DEFAULT_UNAUTHORIZED_COMPONENT_INSTANTIATION_LISTENER = new IUnauthorizedComponentInstantiationListener()
{
@@ -275,4 +291,52 @@ public class SecuritySettings
authenticationStrategy = strategy;
return this;
}
+
+ public CrossOriginOpenerPolicyConfiguration getCrossOriginOpenerPolicyConfiguration()
+ {
+ return crossOriginOpenerPolicyConfiguration;
+ }
+
+ /**
+ * Sets the Cross-Origin Opener Policy's mode and exempted paths. The config values are only
+ * read once at startup in Application#initApplication(), changing the config at runtime will have no effect
+ *
+ * @param mode
+ * CoopMode, one of the 4 values: UNSAFE_NONE, SAME_ORIGIN, SAME_ORIGIN_ALLOW_POPUPS, DISABLED
+ * @param exemptions
+ * exempted paths for which COOP will be disabled
+ * @return
+ */
+ public SecuritySettings setCrossOriginOpenerPolicyConfiguration(
+ CoopMode mode, String... exemptions)
+ {
+ crossOriginOpenerPolicyConfiguration = new CrossOriginOpenerPolicyConfiguration(mode, exemptions);
+ return this;
+ }
+
+
+ public CrossOriginEmbedderPolicyConfiguration getCrossOriginEmbedderPolicyConfiguration()
+ {
+ return crossOriginEmbedderPolicyConfiguration;
+ }
+
+ /**
+ * Sets the Cross-Origin Embedder Policy's mode and exempted paths. The config values are only
+ * read once at startup in Application#initApplication(), changing the config at runtime will
+ * have no effect
+ *
+ * @param mode
+ * CoepMode, one of the 3 values: ENFORCING, REPORTING, DISABLED
+ * @param exemptions
+ * exempted paths for which COEP will be disabled
+ * @return
+ */
+ public SecuritySettings setCrossOriginEmbedderPolicyConfiguration(CoepMode mode,
+ String... exemptions)
+ {
+ crossOriginEmbedderPolicyConfiguration = new CrossOriginEmbedderPolicyConfiguration(mode,
+ exemptions);
+ return this;
+ }
+
}
diff --git a/wicket-core/src/test/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListenerTest.java
new file mode 100644
index 0000000..4e0ea79
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/coep/CrossOriginEmbedderPolicyRequestCycleListenerTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coep;
+
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.jupiter.api.Test;
+
+import org.apache.wicket.coep.CrossOriginEmbedderPolicyConfiguration.CoepMode;
+
+import static org.apache.wicket.coep.CrossOriginEmbedderPolicyRequestCycleListener.REQUIRE_CORP;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class CrossOriginEmbedderPolicyRequestCycleListenerTest extends WicketTestCase
+{
+ private CoepMode mode;
+ private String exemptions;
+
+ @Test
+ void testEnforcingCoepHeadersSetCorrectly()
+ {
+ mode = CoepMode.ENFORCING;
+ buildApp();
+ checkHeaders(CoepMode.ENFORCING);
+ }
+
+ @Test
+ void testReportingCoepHeadersSetCorrectly()
+ {
+ mode = CoepMode.REPORTING;
+ buildApp();
+ checkHeaders(CoepMode.REPORTING);
+ }
+
+ @Test
+ void testCoepDisabled()
+ {
+ mode = CoepMode.DISABLED;
+ buildApp();
+ tester.executeUrl("exempt");
+ String coepHeaderValue = tester.getLastResponse().getHeader(CoepMode.REPORTING.header);
+ assertNull(coepHeaderValue, "COOP header should be null on DISABLED");
+ }
+
+ @Test
+ void testCoepHeadersNotSetExemptedPath()
+ {
+ mode = CoepMode.DISABLED;
+ exemptions = "exempt";
+ buildApp();
+ tester.executeUrl("exempt");
+ String coepHeaderValue = tester.getLastResponse().getHeader(CoepMode.REPORTING.header);
+
+ assertNull(coepHeaderValue, "COOP header should be null on exempted path");
+ }
+
+ private void checkHeaders(CoepMode mode)
+ {
+ tester.executeUrl("/");
+ String coepHeaderValue = tester.getLastResponse().getHeader(mode.header);
+
+ assertNotNull(coepHeaderValue, "COEP " + mode + " header should not be null");
+
+ assertEquals(REQUIRE_CORP, coepHeaderValue, "Unexpected COEP header: " + coepHeaderValue);
+ }
+
+ @Override
+ protected WebApplication newApplication()
+ {
+ return new MockApplication()
+ {
+ @Override
+ protected void init()
+ {
+ super.init();
+ getSecuritySettings().setCrossOriginEmbedderPolicyConfiguration(mode, exemptions);
+ }
+ };
+ }
+
+ // overriding the commonBefore because we want to modify init behavior
+ // contents of commonBefore moved to buildApp, called after the coepMode / exemption set in every test
+ @Override
+ public void commonBefore()
+ {
+ }
+
+ private void buildApp()
+ {
+ ThreadContext.detach();
+
+ WebApplication application = newApplication();
+ tester = newWicketTester(application);
+ }
+}
diff --git a/wicket-core/src/test/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListenerTest.java
new file mode 100644
index 0000000..0cf544e
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/coop/CrossOriginOpenerPolicyRequestCycleListenerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.coop;
+
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.apache.wicket.coop.CrossOriginOpenerPolicyConfiguration.CoopMode;
+import org.junit.jupiter.api.Test;
+
+
+import static org.apache.wicket.coop.CrossOriginOpenerPolicyRequestCycleListener.COOP_HEADER;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class CrossOriginOpenerPolicyRequestCycleListenerTest extends WicketTestCase
+{
+ private CoopMode mode;
+ private String exemptions;
+
+ @Test
+ void testCoopHeaderSameOrigin()
+ {
+ mode = CoopMode.SAME_ORIGIN;
+ buildApp();
+ checkHeaders(CoopMode.SAME_ORIGIN);
+ }
+
+ @Test
+ void testCoopHeaderSameOriginAllowPopups()
+ {
+ mode = CoopMode.SAME_ORIGIN_ALLOW_POPUPS;
+ buildApp();
+ checkHeaders(CoopMode.SAME_ORIGIN_ALLOW_POPUPS);
+ }
+
+ @Test
+ void testCoopHeaderUnsafeNone()
+ {
+ mode = CoopMode.UNSAFE_NONE;
+ buildApp();
+ checkHeaders(CoopMode.UNSAFE_NONE);
+ }
+
+ @Test
+ void testCoopDisabled()
+ {
+ mode = CoopMode.DISABLED;
+ buildApp();
+ tester.executeUrl("/");
+ String coopHeaderValue = tester.getLastResponse().getHeader(COOP_HEADER);
+
+ assertNull(coopHeaderValue, "COOP header should be null on DISABLED");
+ }
+
+ @Test
+ void testCoopHeadersNotSetExemptedPath()
+ {
+ mode = CoopMode.DISABLED;
+ exemptions = "exempt";
+ buildApp();
+ tester.executeUrl("exempt");
+ String coopHeaderValue = tester.getLastResponse().getHeader(COOP_HEADER);
+
+ assertNull(coopHeaderValue, "COOP header should be null on exempted path");
+ }
+
+ private void checkHeaders(CoopMode mode)
+ {
+ tester.executeUrl("/");
+ String coopHeaderValue = tester.getLastResponse().getHeader(COOP_HEADER);
+
+ assertNotNull(coopHeaderValue, "COOP header should not be null");
+
+ assertEquals(mode.keyword, coopHeaderValue, "Unexpected COOP header: " + coopHeaderValue);
+ }
+
+ @Override
+ protected WebApplication newApplication()
+ {
+ return new MockApplication()
+ {
+ @Override
+ protected void init()
+ {
+ super.init();
+ getSecuritySettings().setCrossOriginOpenerPolicyConfiguration(mode, exemptions);
+ }
+ };
+ }
+
+ // overriding the commonBefore because we want to modify init behavior
+ // contents of commonBefore moved to buildApp, called after the coopMode/exemption set in every test
+ @Override
+ public void commonBefore()
+ {
+ }
+
+ private void buildApp()
+ {
+ ThreadContext.detach();
+
+ WebApplication application = newApplication();
+ tester = newWicketTester(application);
+ }
+}