diff --git a/openam-core/src/main/java/org/forgerock/openam/services/baseurl/BaseURLProvider.java b/openam-core/src/main/java/org/forgerock/openam/services/baseurl/BaseURLProvider.java index 8024ffb8e2..71d207c0a8 100644 --- a/openam-core/src/main/java/org/forgerock/openam/services/baseurl/BaseURLProvider.java +++ b/openam-core/src/main/java/org/forgerock/openam/services/baseurl/BaseURLProvider.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2023 3A Systems LLC. */ package org.forgerock.openam.services.baseurl; @@ -98,7 +99,7 @@ public String getRootURL(HttpContext context) { */ public String getRealmURL(HttpServletRequest request, String basePath, Realm realm) throws InvalidBaseUrlException { try { - Realm dnsRealm = Realm.of(URI.create(request.getRequestURI()).getHost()); + Realm dnsRealm = Realm.of(URI.create(request.getRequestURL().toString()).getHost()); return getRealmURL(getRootURL(request), basePath, dnsRealm, realm); } catch (RealmLookupException e) { throw new InvalidBaseUrlException(e.getMessage(), e); diff --git a/openam-core/src/main/resources/amCORS.properties b/openam-core/src/main/resources/amCORS.properties new file mode 100644 index 0000000000..d25403a2c4 --- /dev/null +++ b/openam-core/src/main/resources/amCORS.properties @@ -0,0 +1,46 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright (c) 2009 Sun Microsystems Inc. All Rights Reserved +# +# The contents of this file are subject to the terms +# of the Common Development and Distribution License +# (the License). You may not use this file except in +# compliance with the License. +# +# You can obtain a copy of the License at +# https://opensso.dev.java.net/public/CDDLv1.0.html or +# opensso/legal/CDDLv1.0.txt +# See the License for the specific language governing +# permission and limitations under the License. +# +# When distributing Covered Code, include this CDDL +# Header Notice in each file and include the License file +# at opensso/legal/CDDLv1.0.txt. +# If applicable, add the following below the CDDL Header, +# with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# $Id: amCORS.properties,v 1.2 2024/02/16 01:29:24 bigfatrat Exp $ +# +# Portions Copyrighted 2024 3A Systems LLC + +cors-service-description=CORS Settings +a101=Enabled +a101.help=Enable / Disable CORS +a102=Allowed Origins +a102.help=Origins from which to accept CORS requests. +a103=Accepted Methods +a103.help=HTTP methods for which to accept CORS requests. +a104=Allowed Headers +a104.help=HTTP headers which can be included in the requests. +a105=Exposed Headers +a105.help=HTTP headers which the user-agent can expose to its CORS client. +a106=Maximum Cache Age +a106.help=Maximum time that the CORS client can cache the pre-flight response, in seconds. +a107=Allow Credentials +a107.help=Whether to include the Vary (Origin) and Access-Control-Allow-Credentials headers in the response. +a108=Expected Hostname +a108A.help=The name of the host expected in the request Host header. + diff --git a/openam-rest/src/main/java/org/forgerock/openam/cors/CORSConfigListener.java b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSConfigListener.java new file mode 100644 index 0000000000..af6e78c942 --- /dev/null +++ b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSConfigListener.java @@ -0,0 +1,109 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2024 3A Systems LLC. + */ + +package org.forgerock.openam.cors; + +import com.iplanet.sso.SSOToken; +import com.sun.identity.security.AdminTokenAction; +import com.sun.identity.shared.datastruct.CollectionHelper; +import com.sun.identity.shared.debug.Debug; +import com.sun.identity.sm.ServiceConfig; +import com.sun.identity.sm.ServiceConfigManager; +import com.sun.identity.sm.ServiceListener; + +import java.security.AccessController; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CORSConfigListener implements ServiceListener { + + private static final Debug debug = Debug.getInstance("frRest"); + + private final static int DEFAULT_TIMEOUT = 600; //10 mins + + private ServiceConfigManager schemaManager; + + private CORSService corsService; + + public CORSConfigListener() { + try { + SSOToken token = AccessController.doPrivileged(AdminTokenAction.getInstance()); + ServiceConfigManager schemaManager = + new ServiceConfigManager( + "CORSService", token); + + register(schemaManager); + } catch (Exception e) { + debug.error("Cannot get ServiceConfigManager - cannot register default version config listener", e); + } + } + + public void register(ServiceConfigManager schemaManager) { + this.schemaManager = schemaManager; + updateSettings(); + if (this.schemaManager.addListener(this) == null) { + debug.error("Could not add listener to ServiceConfigManager instance. Version behaviour changes will not " + + "be dynamically updated"); + } + } + + private void updateSettings() { + try { + ServiceConfig serviceConfig = schemaManager.getGlobalConfig(null); + Map> attrs = serviceConfig.getAttributes(); + + final boolean enabled = CollectionHelper.getBooleanMapAttr(attrs, "cors-enabled", false); + final List allowedOrigins = new ArrayList<>(attrs.get("allowed-origins")); + final List acceptedMethods = new ArrayList<>(attrs.get("accepted-methods")); + final List acceptedHeaders = new ArrayList<>(attrs.get("accepted-headers")); + final List exposedHeaders =new ArrayList<>(attrs.get("exposed-headers")); + final String expectedHostname = CollectionHelper.getMapAttr(attrs, "expected-hostname"); + final boolean allowCredentials = CollectionHelper.getBooleanMapAttr(attrs, "allow-credentials", false); + + final int maxAge = CollectionHelper.getIntMapAttr(attrs, "max-age", DEFAULT_TIMEOUT, debug); + if (debug.messageEnabled()) { + debug.message("Successfully updated CORS settings"); + } + + corsService = new CORSService(enabled, allowedOrigins, acceptedMethods, acceptedHeaders, + exposedHeaders, maxAge, allowCredentials, expectedHostname); + } catch (Exception e) { + debug.error("Not able to set version behaviour for rest endpoints", e); + } + } + + + public CORSService getCorsService() { + return this.corsService; + } + + @Override + public void schemaChanged(String serviceName, String version) { + + } + + @Override + public void globalConfigChanged(String serviceName, String version, String groupName, String serviceComponent, int type) { + updateSettings(); + } + + @Override + public void organizationConfigChanged(String serviceName, String version, String orgName, String groupName, String serviceComponent, int type) { + + } +} diff --git a/openam-rest/src/main/java/org/forgerock/openam/cors/CORSFilter.java b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSFilter.java index ff691fb22c..850560e80b 100644 --- a/openam-rest/src/main/java/org/forgerock/openam/cors/CORSFilter.java +++ b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSFilter.java @@ -12,11 +12,12 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014 ForgeRock AS. +* Portions Copyrighted 2024 3A Systems LLC. */ package org.forgerock.openam.cors; -import java.io.IOException; -import java.util.List; +import org.forgerock.guice.core.InjectorHolder; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -25,8 +26,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.forgerock.openam.cors.utils.CSVHelper; -import org.forgerock.util.Reject; +import java.io.IOException; /** * A Servlet Filter implementation of the CORS Service, initialized using a FilterConfig. @@ -35,75 +35,17 @@ */ public class CORSFilter implements Filter { - private CORSService service; - private final CSVHelper csvHelper = new CSVHelper(); - - private final static int DEFAULT_TIMEOUT = 600; //10 mins - /** * Required default constructor */ public CORSFilter() { } - /** - * Testing constructor - * @param service Service through which to validate requests and alter responses applying the CORS spec. - */ - CORSFilter(final CORSService service) { - this.service = service; - } - /** * {@inheritDoc} */ @Override public void init(final FilterConfig filterConfig) throws ServletException { - Reject.ifTrue(filterConfig == null, "Configuration must not be null."); - - final String allowedOrigins = filterConfig.getInitParameter(CORSConstants.ORIGINS_KEY); - final String allowedMethods = filterConfig.getInitParameter(CORSConstants.METHODS_KEY); - final String allowedHeaders = filterConfig.getInitParameter(CORSConstants.HEADERS_KEY); - final String exposedHeaderStr = filterConfig.getInitParameter(CORSConstants.EXPOSE_HEADERS_KEY); - final String expectedHostname = filterConfig.getInitParameter(CORSConstants.EXPECTED_HOSTNAME_KEY); - - int maxAge = DEFAULT_TIMEOUT; - boolean allowCredentials = false; - - if (allowedOrigins == null || allowedOrigins.isEmpty()) { - throw new ServletException("Invalid configuration. Allowed Origins must be set."); - } - - if (allowedMethods == null || allowedMethods.isEmpty()) { - throw new ServletException("Invalid configuration. Allowed Methods must be set."); - } - - final List acceptedOrigins = csvHelper.csvStringToList(allowedOrigins, false); - final List acceptedMethods = csvHelper.csvStringToList(allowedMethods, false); - - if (!acceptedMethods.contains(CORSConstants.HTTP_OPTIONS)) { - acceptedMethods.add(CORSConstants.HTTP_OPTIONS); //always includes OPTIONS for PRE-FLIGHT flow - } - - final List acceptedHeaders = csvHelper.csvStringToList(allowedHeaders, true); - final List exposedHeaders = csvHelper.csvStringToList(exposedHeaderStr, true); - - //defaults to 0, and isn't included - if (filterConfig.getInitParameter(CORSConstants.MAX_AGE_KEY) != null) { - try { - maxAge = Integer.valueOf(filterConfig.getInitParameter(CORSConstants.MAX_AGE_KEY)); - } catch (NumberFormatException e) { - throw new ServletException("Invalid configuration. Max-age must be an integer.", e); - } - } - - //defaults to false - if (filterConfig.getInitParameter(CORSConstants.ALLOW_CREDENTIALS_KEY) != null) { - allowCredentials = Boolean.valueOf(filterConfig.getInitParameter(CORSConstants.ALLOW_CREDENTIALS_KEY)); - } - - service = new CORSService(acceptedOrigins, acceptedMethods, acceptedHeaders, - exposedHeaders, maxAge, allowCredentials, expectedHostname); } /** @@ -116,10 +58,12 @@ public void doFilter(final ServletRequest request, final ServletResponse respons final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; - if (service.handleRequest(req, res)) { + final CORSConfigListener corsConfigListener = InjectorHolder.getInstance(CORSConfigListener.class); + final CORSService corsService = corsConfigListener.getCorsService(); + + if (corsService == null || corsConfigListener.getCorsService().handleRequest(req, res)) { chain.doFilter(req, res); } - } /** @@ -127,6 +71,6 @@ public void doFilter(final ServletRequest request, final ServletResponse respons */ @Override public void destroy() { - service = null; + } } diff --git a/openam-rest/src/main/java/org/forgerock/openam/cors/CORSService.java b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSService.java index cd26fd43a0..d1893a79da 100644 --- a/openam-rest/src/main/java/org/forgerock/openam/cors/CORSService.java +++ b/openam-rest/src/main/java/org/forgerock/openam/cors/CORSService.java @@ -15,21 +15,20 @@ */ package org.forgerock.openam.cors; +import com.sun.identity.shared.debug.Debug; +import org.apache.commons.collections4.CollectionUtils; +import org.forgerock.openam.cors.utils.CSVHelper; +import org.forgerock.util.Reject; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.Enumeration; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.forgerock.openam.cors.utils.CSVHelper; -import org.forgerock.util.Reject; - -import com.sun.identity.shared.debug.Debug; +import java.util.stream.Collectors; /** * See http://www.w3.org/TR/cors/ for further information. @@ -54,6 +53,8 @@ public class CORSService { private final CSVHelper csvHelper = new CSVHelper(); + private final boolean enabled; + private final List acceptedOrigins; private final List acceptedMethods; private final List acceptedHeaders; @@ -77,12 +78,24 @@ public class CORSService { * @param expectedHostname (Optional) the name of the host the request should be headed to in its Host header * @throws IllegalArgumentException If the acceptedOrigins or acceptedMethods params are null or empty */ - public CORSService(final List acceptedOrigins, final List acceptedMethods, + public CORSService(final boolean enabled, List acceptedOrigins, List acceptedMethods, List acceptedHeaders, List exposedHeaders, int maxAge, final boolean allowCredentials, String expectedHostname) { - Reject.ifTrue(acceptedOrigins == null || acceptedOrigins.size() < 1, "AcceptedOrigins must have at least one value."); - Reject.ifTrue(acceptedMethods == null || acceptedMethods.size() < 1, "AcceptedOrigins must have at least one value."); + + this.enabled = enabled; + + if(enabled) { + Reject.ifTrue(acceptedOrigins == null || acceptedOrigins.size() < 1, "AcceptedOrigins must have at least one value."); + Reject.ifTrue(acceptedMethods == null || acceptedMethods.size() < 1, "AcceptedOrigins must have at least one value."); + } + + if(acceptedOrigins == null) { + acceptedOrigins = new ArrayList<>(); + } + if(acceptedMethods == null) { + acceptedMethods = new ArrayList<>(); + } if (maxAge < 0) { maxAge = 0; @@ -98,7 +111,7 @@ public CORSService(final List acceptedOrigins, final List accept this.acceptedOrigins = acceptedOrigins; this.acceptedMethods = acceptedMethods; - this.acceptedHeaders = acceptedHeaders; + this.acceptedHeaders = acceptedHeaders.stream().map(String::toLowerCase).collect(Collectors.toList()); this.exposedHeaders = exposedHeaders; this.allowCredentials = allowCredentials; this.maxAge = maxAge; @@ -115,6 +128,9 @@ public CORSService(final List acceptedOrigins, final List accept * @return true if the caller is to continue processing the request */ public boolean handleRequest(final HttpServletRequest req, final HttpServletResponse res) { + if(!this.enabled) { + return true; + } if (req.getHeader(CORSConstants.ORIGIN) == null) { return true; } @@ -146,7 +162,6 @@ public boolean handleRequest(final HttpServletRequest req, final HttpServletResp * * @param req The request * @param res The response - * @return true if the request was valid and successfully handled */ private void handlePreflightFlow(final HttpServletRequest req, final HttpServletResponse res) { @@ -183,18 +198,6 @@ private void handlePreflightFlow(final HttpServletRequest req, final HttpServlet */ private boolean handleActualRequestFlow(final HttpServletRequest req, final HttpServletResponse res) { - Enumeration headerNames = req.getHeaderNames(); - - if (headerNames != null) { - while (headerNames.hasMoreElements()) { - String header = headerNames.nextElement(); - if (!acceptedHeaders.contains(header.toLowerCase()) && !simpleHeaders.contains(header.toLowerCase())) { - DEBUG.warning("CORS Fail - Headers do not match allowed headers."); - return false; - } - } - } - final String originHeader = req.getHeader(CORSConstants.ORIGIN); if(exposedHeaders.size() > 0) { @@ -292,6 +295,10 @@ private boolean isValidCORSRequest(final HttpServletRequest req) { DEBUG.warning("CORS Fail - Request did not contain an origin header."); return false; } + if(CollectionUtils.isEmpty(acceptedOrigins)) { + DEBUG.warning("CORS Fail - Accepted Origins setting is empty"); + return false; + } if (!acceptedOrigins.contains(CORSConstants.ALL)) { //if we're from a valid origin or not @@ -307,8 +314,13 @@ private boolean isValidCORSRequest(final HttpServletRequest req) { return false; } + if(CollectionUtils.isEmpty(acceptedMethods)) { + DEBUG.warning("CORS Fail - Accepted Method setting is empty"); + return false; + } + //then look to see if one of the method types allowed for CORS from the configuration - if (!acceptedMethods.contains(req.getMethod())) { + if (!acceptedMethods.contains(req.getMethod()) && !CORSConstants.HTTP_OPTIONS.equals(req.getMethod())) { DEBUG.warning("CORS Fail - Requested HTTP method has not been whitelisted."); return false; } diff --git a/openam-rest/src/main/java/org/forgerock/openam/rest/RestGuiceModule.java b/openam-rest/src/main/java/org/forgerock/openam/rest/RestGuiceModule.java index 3fff68c46d..2845f40c80 100644 --- a/openam-rest/src/main/java/org/forgerock/openam/rest/RestGuiceModule.java +++ b/openam-rest/src/main/java/org/forgerock/openam/rest/RestGuiceModule.java @@ -56,6 +56,7 @@ import org.forgerock.json.resource.RequestHandler; import org.forgerock.json.resource.Router; import org.forgerock.openam.audit.HttpAccessAuditFilterFactory; +import org.forgerock.openam.cors.CORSConfigListener; import org.forgerock.openam.rest.fluent.AuditFilter; import org.forgerock.openam.rest.fluent.CrestLoggingFilter; import org.forgerock.openam.rest.resource.SSOTokenContext; @@ -76,6 +77,7 @@ public class RestGuiceModule extends AbstractModule { protected void configure() { bind(Debug.class).annotatedWith(Names.named("frRest")).toInstance(Debug.getInstance("frRest")); bind(ResourceApiVersionBehaviourManager.class).to(VersionBehaviourConfigListener.class).in(Singleton.class); + bind(CORSConfigListener.class).in(Singleton.class); bind(Key.get(Filter.class, Names.named("LoggingFilter"))).to(CrestLoggingFilter.class).in(Singleton.class); bind(Key.get(Logger.class, Names.named("RestAuthentication"))) .toInstance(LoggerFactory.getLogger("restAuthenticationFilter")); diff --git a/openam-rest/src/test/java/org/forgerock/openam/cors/CORSFilterTest.java b/openam-rest/src/test/java/org/forgerock/openam/cors/CORSFilterTest.java deleted file mode 100644 index ee36cec047..0000000000 --- a/openam-rest/src/test/java/org/forgerock/openam/cors/CORSFilterTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* -* The contents of this file are subject to the terms of the Common Development and -* Distribution License (the License). You may not use this file except in compliance with the -* License. -* -* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the -* specific language governing permission and limitations under the License. -* -* When distributing Covered Software, include this CDDL Header Notice in each file and include -* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL -* Header, with the fields enclosed by brackets [] replaced by your own identifying -* information: "Portions copyright [year] [name of copyright owner]". -* -* Copyright 2014 ForgeRock AS. -*/ -package org.forgerock.openam.cors; - -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class CORSFilterTest { - - private CORSService mockService = mock(CORSService.class); - private CORSFilter testFilter; - - @BeforeMethod - public void setUp() { - this.testFilter = new CORSFilter(mockService); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void shouldThrowExceptionWhenNullConfig() throws ServletException { - //given - - //when - testFilter.init(null); - - //then - } - - @Test(expectedExceptions = ServletException.class) - public void shouldThrowExceptionWhenNoConfig() throws ServletException { - //given - FilterConfig config = mock(FilterConfig.class); - - //when - testFilter.init(config); - - //then - } - - @Test(expectedExceptions = ServletException.class) - public void shoudlThrowExceptionWhenNoMethodsInConfig() throws ServletException { - //given - FilterConfig config = mock(FilterConfig.class); - given(config.getInitParameter(CORSConstants.ORIGINS_KEY)).willReturn("www.google.com"); - - //when - testFilter.init(config); - - //then - - } - - @Test(expectedExceptions = ServletException.class) - public void shouldThrowExceptionWhenNoOriginInConfig() throws ServletException { - //given - FilterConfig config = mock(FilterConfig.class); - given(config.getInitParameter(CORSConstants.METHODS_KEY)).willReturn("GET,POST"); - - //when - testFilter.init(config); - - //then - } - - @Test(expectedExceptions = ServletException.class) - public void shouldThrowExceptionWhenMaxAgeIsNaN() throws ServletException { - //given - FilterConfig config = mock(FilterConfig.class); - given(config.getInitParameter(CORSConstants.ORIGINS_KEY)).willReturn("www.google.com"); - given(config.getInitParameter(CORSConstants.METHODS_KEY)).willReturn("GET,POST"); - given(config.getInitParameter(CORSConstants.MAX_AGE_KEY)).willReturn("words"); - - //when - testFilter.init(config); - - //then - } - - @Test - public void shouldConfigureWithBothMethodsAndOriginsNoErrors() { - //given - FilterConfig config = mock(FilterConfig.class); - given(config.getInitParameter(CORSConstants.ORIGINS_KEY)).willReturn("www.google.com"); - given(config.getInitParameter(CORSConstants.METHODS_KEY)).willReturn("GET,POST"); - boolean success = true; - - //when - try { - testFilter.init(config); - } catch (ServletException e) { - success = false; - } - - //then - assertTrue(success); - } - -} diff --git a/openam-rest/src/test/java/org/forgerock/openam/cors/CORSServiceTest.java b/openam-rest/src/test/java/org/forgerock/openam/cors/CORSServiceTest.java index ed4a01bf0d..c392275fe8 100644 --- a/openam-rest/src/test/java/org/forgerock/openam/cors/CORSServiceTest.java +++ b/openam-rest/src/test/java/org/forgerock/openam/cors/CORSServiceTest.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014 ForgeRock AS. +* Portions Copyrighted 2024 3A Systems LLC. */ package org.forgerock.openam.cors; @@ -37,9 +38,9 @@ public class CORSServiceTest { @BeforeMethod public void setUp() { - ArrayList origins = new ArrayList(); + ArrayList origins = new ArrayList<>(); origins.add("www.google.com"); - ArrayList methods = new ArrayList(); + ArrayList methods = new ArrayList<>(); methods.add("POST"); methods.add("DELETE"); methods.add("OPTIONS"); @@ -48,7 +49,7 @@ public void setUp() { ArrayList exposedHeaders = new ArrayList(); exposedHeaders.add("x-posed-header"); - testService = new CORSService(origins, methods, acceptedHeaders, + testService = new CORSService(true, origins, methods, acceptedHeaders, exposedHeaders, 999, true, null); mockRequest = mock(HttpServletRequest.class); @@ -62,7 +63,7 @@ public void NullOriginsThrowIllegalArgument() { list.add("one"); //when - CORSService service = new CORSService(null, list, list, list, 0, false, null); + CORSService service = new CORSService(true, null, list, list, list, 0, false, null); //then @@ -76,7 +77,7 @@ public void EmptyOriginsThrowIllegalArgument() { ArrayList list2 = new ArrayList(); //when - CORSService service = new CORSService(list2, list, list, list, 0, false, null); + CORSService service = new CORSService(true, list2, list, list, list, 0, false, null); //then } @@ -88,7 +89,7 @@ public void NullMethodsThrowIllegalArgument() { list.add("one"); //when - CORSService service = new CORSService(list, null, list, list, 0, false, null); + CORSService service = new CORSService(true, list, null, list, list, 0, false, null); //then } @@ -101,7 +102,7 @@ public void EmptyMethodsThrowIllegalArgument() { ArrayList list2 = new ArrayList(); //when - CORSService service = new CORSService(list, list2, list, list, 0, false, null); + CORSService service = new CORSService(true, list, list2, list, list, 0, false, null); //then } @@ -192,7 +193,7 @@ public void shouldFollowNormalFlowJustApplyOrigin() { ArrayList methods = new ArrayList(); methods.add("POST"); - testService = new CORSService(origins, methods, null, null, 0, false, null); + testService = new CORSService(true, origins, methods, null, null, 0, false, null); given(mockRequest.getHeader(CORSConstants.ORIGIN)).willReturn("www.google.com"); given(mockRequest.getMethod()).willReturn("POST"); @@ -271,7 +272,7 @@ public void testInvalidHostnameFailsValidation() { exposedHeaders.add("x-posed-header"); exposedHeaders.add("x-posed-header2"); - testService = new CORSService(origins, methods, acceptedHeaders, + testService = new CORSService(true, origins, methods, acceptedHeaders, exposedHeaders, 0, true, "www.openam.com"); given(mockRequest.getHeader(CORSConstants.ORIGIN)).willReturn("www.google.com"); @@ -298,7 +299,7 @@ public void testHandleNormalIncludesExposedHeadersInResponse() { exposedHeaders.add("x-posed-header"); exposedHeaders.add("x-posed-header2"); - testService = new CORSService(origins, methods, acceptedHeaders, + testService = new CORSService(true, origins, methods, acceptedHeaders, exposedHeaders, 0, true, null); given(mockRequest.getHeader(CORSConstants.ORIGIN)).willReturn("www.google.com"); diff --git a/openam-server-only/src/main/resources/config/serviceNames.properties b/openam-server-only/src/main/resources/config/serviceNames.properties index 5d45439fd7..5f0ca6bcc0 100644 --- a/openam-server-only/src/main/resources/config/serviceNames.properties +++ b/openam-server-only/src/main/resources/config/serviceNames.properties @@ -45,7 +45,7 @@ serviceNames=dashboardService.xml amEntrySpecific.xml amAuthConfig.xml amAuthHTT socialAuthN.xml amBaseURL.xml scripting.xml amAuthOATH.xml amAuthSAML2.xml audit.xml RadiusServerService.xml \ amSessionPropertyWhitelist.xml selfService.xml amPushNotification.xml amAuthAuthenticatorPush.xml \ AuthenticatorPush.xml amAuthAuthenticatorPushRegistration.xml amAuthAmster.xml amAuthReCaptcha.xml amAuthSecurID.xml \ - amAuthWebAuthnRegistration.xml amAuthWebAuthnAuthentication.xml amAuthQR.xml amAuthNtlm.xml + amAuthWebAuthnRegistration.xml amAuthWebAuthnAuthentication.xml amAuthQR.xml amAuthNtlm.xml amCORS.xml umEmbeddedDS=idRepoEmbeddedOpenDS.xml umSunDSForAM=idRepoS1DS.xml umSunDSGeneric=idRepoGenericV3.xml diff --git a/openam-server-only/src/main/resources/services/amCORS.xml b/openam-server-only/src/main/resources/services/amCORS.xml new file mode 100644 index 0000000000..3db2ff57db --- /dev/null +++ b/openam-server-only/src/main/resources/services/amCORS.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + false + + + + + * + + + + + GET + POST + PUT + + + + + Origin + Accept + Connection + User-Agent + Host + Accept-Encoding + Accept-Language + Content-Length + Content-Type + Upgrade-Insecure-Requests + Referer + Cookie + + + + + + + 600 + + + + + false + + + + + + + + diff --git a/openam-server-only/src/main/webapp/WEB-INF/web.xml b/openam-server-only/src/main/webapp/WEB-INF/web.xml index 1472331c22..03e2f5a16f 100644 --- a/openam-server-only/src/main/webapp/WEB-INF/web.xml +++ b/openam-server-only/src/main/webapp/WEB-INF/web.xml @@ -87,70 +87,12 @@ /policyEditor/,/policyEditor/index.html,/scripts/,/scripts/index.html,/XUI/,/XUI/index.html - + AuthNFilter com.sun.identity.rest.AuthNFilter @@ -270,12 +212,16 @@ CacheForAMonth /XUI/* - + + CORSFilter + /oauth2/* + + AuthNFilter /ws/* diff --git a/openam-server/pom.xml b/openam-server/pom.xml index 09b3f90dfc..8191af135d 100644 --- a/openam-server/pom.xml +++ b/openam-server/pom.xml @@ -157,12 +157,6 @@ 4.13.0 test - - io.github.bonigarcia - webdrivermanager - 5.5.3 - test - diff --git a/openam-server/src/test/java/org/openidentityplatform/openam/test/integration/IT_Setup.java b/openam-server/src/test/java/org/openidentityplatform/openam/test/integration/IT_Setup.java index eb2af90df6..617070132f 100644 --- a/openam-server/src/test/java/org/openidentityplatform/openam/test/integration/IT_Setup.java +++ b/openam-server/src/test/java/org/openidentityplatform/openam/test/integration/IT_Setup.java @@ -1,7 +1,5 @@ package org.openidentityplatform.openam.test.integration; -import io.github.bonigarcia.wdm.WebDriverManager; -import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.StaleElementReferenceException; @@ -33,7 +31,6 @@ public class IT_Setup { @BeforeTest public void webdriverSetup() { - WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions(); options.addArguments("--remote-allow-origins=*","--headless", "--disable-dev-shm-usage", "--no-sandbox", "--verbose"); driver = new ChromeDriver(options); @@ -127,7 +124,7 @@ public boolean isTextPresent(String str) { for(int i = 0; i < 3; i++) { try { WebElement bodyElement = new WebDriverWait(driver,Duration.ofSeconds(20)).until(ExpectedConditions.presenceOfElementLocated(By.tagName("body"))); - return StringUtils.containsIgnoreCase(bodyElement.getText(), str); + return bodyElement.getText().toLowerCase().contains(str.toLowerCase()); } catch (StaleElementReferenceException e) { lastException = e; }