diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/CrossOriginIsolationFilter.java b/src/main/java/ch/ethz/seb/sebserver/gui/CrossOriginIsolationFilter.java new file mode 100644 index 00000000..fb9e92ce --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/CrossOriginIsolationFilter.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; + +@Lazy +@Component +@GuiProfile +public class CrossOriginIsolationFilter implements Filter { + + private static final String SAME_ORIGIN = "same-origin"; + private static final String REQUIRE_CORP = "require-corp"; + private static final String CROSS_ORIGIN_OPENER_POLICY = "Cross-Origin-Opener-Policy"; + private static final String CROSS_ORIGIN_EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy"; + + private final String adminEndpoint; + private final String examEndpoint; + + public CrossOriginIsolationFilter( + @Value("${sebserver.webservice.api.exam.endpoint:/exam-api}") final String examEndpoint, + @Value("${sebserver.webservice.api.admin.endpoint:/admin-api/v1}") final String adminEndpoint) { + + this.adminEndpoint = adminEndpoint; + this.examEndpoint = examEndpoint; + } + + @Override + public void doFilter( + final ServletRequest request, + final ServletResponse response, + final FilterChain chain) throws IOException, ServletException { + + if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { + chain.doFilter(request, response); + return; + } + + processResponse((HttpServletRequest) request, (HttpServletResponse) response); + chain.doFilter(request, response); + } + + protected void processResponse(final HttpServletRequest request, final HttpServletResponse response) { + final String url = request.getRequestURI(); + + if (url.startsWith(this.adminEndpoint) || url.startsWith(this.examEndpoint)) { + return; + } + + response.setHeader(CROSS_ORIGIN_EMBEDDER_POLICY, REQUIRE_CORP); + response.setHeader(CROSS_ORIGIN_OPENER_POLICY, SAME_ORIGIN); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java index b0d685c9..4adaaf05 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java @@ -16,6 +16,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.access.channel.ChannelProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -30,6 +31,8 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private InstitutionalAuthenticationEntryPoint institutionalAuthenticationEntryPoint; + @Autowired + private CrossOriginIsolationFilter crossOriginIsolationFilter; @Value("${sebserver.gui.entrypoint:/gui}") private String guiEntryPoint; @@ -84,7 +87,10 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { .logout().disable() .headers().frameOptions().disable() .and() - .csrf().disable(); + .csrf() + .disable() + // TODO Set filter to dedicated URL + .addFilterBefore(this.crossOriginIsolationFilter, ChannelProcessingFilter.class); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java index de6c66ac..73321a3e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java @@ -78,7 +78,6 @@ public class ProctoringServlet extends HttpServlet { log.error("Failed to get proctoring window script for data: {}", proctoringData); resp.getOutputStream().println("Failed to get proctoring window script"); } else { - RAPConfiguration.setCORS(resp); resp.getOutputStream().println(script); } } @@ -87,7 +86,6 @@ public class ProctoringServlet extends HttpServlet { protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { - RAPConfiguration.setCORS(resp); resp.setStatus(HttpServletResponse.SC_OK); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/RAPConfiguration.java b/src/main/java/ch/ethz/seb/sebserver/gui/RAPConfiguration.java index 5dd022ca..ef44681b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/RAPConfiguration.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/RAPConfiguration.java @@ -125,8 +125,6 @@ public class RAPConfiguration implements ApplicationConfiguration { final EntryPointService entryPointService = webApplicationContext .getBean(EntryPointService.class); entryPointService.loadProctoringView(parent); - final HttpServletResponse response = RWT.getResponse(); - setCORS(response); } else { final HttpServletResponse response = RWT.getResponse(); response.setStatus(HttpStatus.FORBIDDEN.value()); @@ -136,16 +134,6 @@ public class RAPConfiguration implements ApplicationConfiguration { } } - // https://developer.chrome.com/blog/enabling-shared-array-buffer/#cross-origin-isolation - public static final void setCORS(final HttpServletResponse resp) { -// resp.addHeader("Access-Control-Allow-Origin", "*"); -// resp.setHeader("Access-Control-Allow-Methods", "GET"); -// resp.setHeader("Vary", "Origin"); - - resp.addHeader("Cross-Origin-Embedder-Policy", "require-corp"); - resp.addHeader("Cross-Origin-Opener-Policy", "same-origin"); - } - public static final class RAPSpringEntryPointFactory implements EntryPointFactory { private boolean initialized = false; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java index d8a52dce..29b160de 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java @@ -195,6 +195,9 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol return false; } + // TODO check if this is needed. If not remove it. + // This gets called many times for a page load + try { final ResponseEntity forEntity = this.restTemplate.getForEntity(this.currentUserURI, String.class);