From 76ce1db95053f2df54ce74e3eeb767fd6369cb1c Mon Sep 17 00:00:00 2001 From: Aleksandra Onych Date: Fri, 4 Mar 2022 09:18:36 +0100 Subject: [PATCH] Feature/acs 2562 disallow secure comms none (#994) ACS-2562 Disallow SecureComms=none Co-authored-by: Domenico Sibilio Co-authored-by: Stefan Kopf --- amps/ags/rm-community/rm-community-repo/.env | 2 +- .../rm-community-repo/docker-compose.yml | 21 +- .../local/alfresco-global.properties | 3 +- packaging/tests/environment/.env | 2 +- .../docker-compose-minimal+transforms.yml | 19 +- .../environment/docker-compose-minimal.yml | 19 +- .../servlet/AlfrescoX509ServletFilter.java | 5 +- .../alfresco/AppContextExtraTestSuite.java | 1 + .../AlfrescoX509ServletFilterTest.java | 190 ++++++++++++++++++ 9 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 remote-api/src/test/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilterTest.java diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env index e50ef550db..3894024a18 100644 --- a/amps/ags/rm-community/rm-community-repo/.env +++ b/amps/ags/rm-community/rm-community-repo/.env @@ -1,4 +1,4 @@ TRANSFORMERS_TAG=2.5.7-A6 -SOLR6_TAG=2.0.2 +SOLR6_TAG=2.0.3-RC2 POSTGRES_TAG=13.3 ACTIVEMQ_TAG=5.16.1 diff --git a/amps/ags/rm-community/rm-community-repo/docker-compose.yml b/amps/ags/rm-community/rm-community-repo/docker-compose.yml index 6cae53e5b7..2b3b10a747 100644 --- a/amps/ags/rm-community/rm-community-repo/docker-compose.yml +++ b/amps/ags/rm-community/rm-community-repo/docker-compose.yml @@ -26,7 +26,8 @@ services: -Ddb.url=jdbc:postgresql://postgres:5432/alfresco -Dsolr.host=search -Dsolr.port=8983 - -Dsolr.secureComms=none + -Dsolr.secureComms=secret + -Dsolr.sharedSecret=secret -Dsolr.base.url=/solr -Dindex.subsystem.name=solr6 -Dalfresco.restApi.basicAuthScheme=true @@ -61,15 +62,19 @@ services: image: alfresco/alfresco-search-services:${SOLR6_TAG} environment: #Solr needs to know how to register itself with Alfresco - - SOLR_ALFRESCO_HOST=alfresco - - SOLR_ALFRESCO_PORT=8080 + SOLR_ALFRESCO_HOST: "alfresco" + SOLR_ALFRESCO_PORT: "8080" #Alfresco needs to know how to call solr - - SOLR_SOLR_HOST=search - - SOLR_SOLR_PORT=8983 + SOLR_SOLR_HOST: "search" + SOLR_SOLR_PORT: "8983" #Create the default alfresco and archive cores - - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive - #HTTP by default - - ALFRESCO_SECURE_COMMS=none + SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" + #HTTP by default + ALFRESCO_SECURE_COMMS: "secret" + JAVA_TOOL_OPTIONS: + " + -Dalfresco.secureComms.secret=secret + " ports: - 8083:8983 #Browser port diff --git a/amps/ags/rm-community/rm-community-repo/src/test/properties/local/alfresco-global.properties b/amps/ags/rm-community/rm-community-repo/src/test/properties/local/alfresco-global.properties index 956533c7fe..6527641a2b 100644 --- a/amps/ags/rm-community/rm-community-repo/src/test/properties/local/alfresco-global.properties +++ b/amps/ags/rm-community/rm-community-repo/src/test/properties/local/alfresco-global.properties @@ -66,7 +66,8 @@ ftp.enabled=false # Solr config index.subsystem.name=solr6 -solr.secureComms=none +solr.secureComms=secret +solr.sharedSecret=secret solr.port=8983 #By default the basic auth is on false - REPO-2575 diff --git a/packaging/tests/environment/.env b/packaging/tests/environment/.env index e50ef550db..3894024a18 100644 --- a/packaging/tests/environment/.env +++ b/packaging/tests/environment/.env @@ -1,4 +1,4 @@ TRANSFORMERS_TAG=2.5.7-A6 -SOLR6_TAG=2.0.2 +SOLR6_TAG=2.0.3-RC2 POSTGRES_TAG=13.3 ACTIVEMQ_TAG=5.16.1 diff --git a/packaging/tests/environment/docker-compose-minimal+transforms.yml b/packaging/tests/environment/docker-compose-minimal+transforms.yml index 676e1e6c9e..1371162217 100644 --- a/packaging/tests/environment/docker-compose-minimal+transforms.yml +++ b/packaging/tests/environment/docker-compose-minimal+transforms.yml @@ -28,7 +28,8 @@ services: -Ddb.url=jdbc:postgresql://postgres:5432/alfresco -Dsolr.host=solr6 -Dsolr.port=8983 - -Dsolr.secureComms=none + -Dsolr.secureComms=secret + -Dsolr.sharedSecret=secret -Dsolr.base.url=/solr -Dindex.subsystem.name=solr6 -Dalfresco.restApi.basicAuthScheme=true @@ -63,15 +64,19 @@ services: image: alfresco/alfresco-search-services:${SOLR6_TAG} environment: #Solr needs to know how to register itself with Alfresco - - SOLR_ALFRESCO_HOST=alfresco - - SOLR_ALFRESCO_PORT=8080 + SOLR_ALFRESCO_HOST: "alfresco" + SOLR_ALFRESCO_PORT: "8080" #Alfresco needs to know how to call solr - - SOLR_SOLR_HOST=solr6 - - SOLR_SOLR_PORT=8983 + SOLR_SOLR_HOST: "solr6" + SOLR_SOLR_PORT: "8983" #Create the default alfresco and archive cores - - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive + SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" #HTTP by default - - ALFRESCO_SECURE_COMMS=none + ALFRESCO_SECURE_COMMS: "secret" + JAVA_TOOL_OPTIONS: + " + -Dalfresco.secureComms.secret=secret + " ports: - 8083:8983 #Browser port diff --git a/packaging/tests/environment/docker-compose-minimal.yml b/packaging/tests/environment/docker-compose-minimal.yml index dd0649c65c..81eec6b7e6 100644 --- a/packaging/tests/environment/docker-compose-minimal.yml +++ b/packaging/tests/environment/docker-compose-minimal.yml @@ -28,7 +28,8 @@ services: -Ddb.url=jdbc:postgresql://postgres:5432/alfresco -Dsolr.host=solr6 -Dsolr.port=8983 - -Dsolr.secureComms=none + -Dsolr.secureComms=secret + -Dsolr.sharedSecret=secret -Dsolr.base.url=/solr -Dindex.subsystem.name=solr6 -Dalfresco.restApi.basicAuthScheme=true @@ -66,15 +67,19 @@ services: image: alfresco/alfresco-search-services:${SOLR6_TAG} environment: #Solr needs to know how to register itself with Alfresco - - SOLR_ALFRESCO_HOST=alfresco - - SOLR_ALFRESCO_PORT=8080 + SOLR_ALFRESCO_HOST: "alfresco" + SOLR_ALFRESCO_PORT: "8080" #Alfresco needs to know how to call solr - - SOLR_SOLR_HOST=solr6 - - SOLR_SOLR_PORT=8983 + SOLR_SOLR_HOST: "solr6" + SOLR_SOLR_PORT: "8983" #Create the default alfresco and archive cores - - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive + SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" #HTTP by default - - ALFRESCO_SECURE_COMMS=none + ALFRESCO_SECURE_COMMS: "secret" + JAVA_TOOL_OPTIONS: + " + -Dalfresco.secureComms.secret=secret + " ports: - 8083:8983 #Browser port diff --git a/remote-api/src/main/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilter.java b/remote-api/src/main/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilter.java index 4c7c8b0c7d..7534ee53af 100644 --- a/remote-api/src/main/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilter.java +++ b/remote-api/src/main/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilter.java @@ -85,8 +85,7 @@ public class AlfrescoX509ServletFilter extends X509ServletFilterBase throw new AlfrescoRuntimeException("Missing value for sharedSecretHeader"); } } - /* - // TODO: Activate this part after OPSEXP-1163 got implemented + if(secureComms == SecureCommsType.NONE) { if(!"true".equalsIgnoreCase(config.getInitParameter("allow-unauthenticated-solr-endpoint"))) @@ -94,7 +93,7 @@ public class AlfrescoX509ServletFilter extends X509ServletFilterBase throw new AlfrescoRuntimeException("solr.secureComms=none is no longer supported. Please use https or secret"); } } - */ + super.init(config); } diff --git a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java index 318b398a7c..badb4c25f4 100644 --- a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java +++ b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java @@ -40,6 +40,7 @@ import org.junit.runners.Suite; org.alfresco.repo.web.scripts.solr.StatsGetTest.class, org.alfresco.repo.web.scripts.solr.SOLRSerializerTest.class, org.alfresco.repo.web.scripts.solr.SOLRAuthenticationFilterTest.class, + org.alfresco.web.app.servlet.AlfrescoX509ServletFilterTest.class, org.alfresco.repo.web.util.PagingCursorTest.class, org.alfresco.repo.web.util.paging.PagingTest.class, org.alfresco.repo.webdav.GetMethodTest.class, diff --git a/remote-api/src/test/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilterTest.java b/remote-api/src/test/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilterTest.java new file mode 100644 index 0000000000..2016e55e08 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/web/app/servlet/AlfrescoX509ServletFilterTest.java @@ -0,0 +1,190 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.web.app.servlet; + +import static junit.framework.TestCase.assertEquals; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import java.util.Properties; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * Unit tests for {@link AlfrescoX509ServletFilter}. + */ +public class AlfrescoX509ServletFilterTest +{ + + private static final String BEAN_GLOBAL_PROPERTIES = "global-properties"; + private static final String PROP_SECURE_COMMS = "solr.secureComms"; + private static final String PROP_SHARED_SECRET = "solr.sharedSecret"; + private static final String PROP_SHARED_SECRET_HEADER = "solr.sharedSecret.header"; + private static final String SHARED_SECRET_HEADER = "X-Alfresco-Search-Secret"; + private static final String SECRET = "secret"; + private static final String ALLOW_UNAUTHORIZED_SOLR_ENDPOINT = "allow-unauthenticated-solr-endpoint"; + + private static final String MISSING_SHARED_SECRET_EXCEPTION_MSG = "Missing value for solr.sharedSecret configuration property"; + private static final String MISSING_SHARED_SECRET_HEADER_EXCEPTION_MSG = "Missing value for sharedSecretHeader"; + private static final String SECURE_COMMS_NONE_IS_NOT_SUPPORTED_EXCEPTION_MSG = "solr.secureComms=none is no longer supported. Please use https or secret"; + + private FilterConfig filterConfig; + private Properties globalProperties; + private AlfrescoX509ServletFilter filter; + + @Before + public void before() + { + FilterConfig filterConfig = Mockito.mock(FilterConfig.class); + WebApplicationContext webApplicationContext = Mockito.mock(WebApplicationContext.class); + ServletContext servletContext = Mockito.mock(ServletContext.class); + Properties globalProperties = Mockito.mock(Properties.class); + + Mockito.when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)).thenReturn(webApplicationContext); + Mockito.when(filterConfig.getServletContext()).thenReturn(servletContext); + Mockito.when(WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext())).thenReturn(webApplicationContext); + Mockito.when(webApplicationContext.getBean(BEAN_GLOBAL_PROPERTIES)).thenReturn(globalProperties); + + this.filterConfig = filterConfig; + this.globalProperties = globalProperties; + this.filter = new AlfrescoX509ServletFilter(); + } + + @Test (expected = AlfrescoRuntimeException.class) + public void testSharedSecretIsEmpty() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.SECRET.name()); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET)).thenReturn(""); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET_HEADER)).thenReturn(SHARED_SECRET_HEADER); + + try + { + filter.init(filterConfig); + } + catch (AlfrescoRuntimeException ex) + { + assertEquals(MISSING_SHARED_SECRET_EXCEPTION_MSG, ex.getMsgId()); + throw ex; + } + } + + @Test (expected = AlfrescoRuntimeException.class) + public void testSharedSecretIsNull() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.SECRET.name()); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET)).thenReturn(null); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET_HEADER)).thenReturn(SHARED_SECRET_HEADER); + + try + { + filter.init(filterConfig); + } + catch (AlfrescoRuntimeException ex) + { + assertEquals(MISSING_SHARED_SECRET_EXCEPTION_MSG, ex.getMsgId()); + throw ex; + } + } + + @Test (expected = AlfrescoRuntimeException.class) + public void testSharedSecretHeaderIsEmpty() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.SECRET.name()); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET)).thenReturn(SECRET); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET_HEADER)).thenReturn(""); + + try + { + filter.init(filterConfig); + } + catch (AlfrescoRuntimeException ex) + { + assertEquals(MISSING_SHARED_SECRET_HEADER_EXCEPTION_MSG, ex.getMsgId()); + throw ex; + } + } + + @Test (expected = AlfrescoRuntimeException.class) + public void testSharedSecretHeaderIsNull() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.SECRET.name()); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET)).thenReturn(SECRET); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET_HEADER)).thenReturn(""); + + try + { + filter.init(filterConfig); + } + catch (AlfrescoRuntimeException ex) + { + assertEquals(MISSING_SHARED_SECRET_HEADER_EXCEPTION_MSG, ex.getMsgId()); + throw ex; + } + } + + @Test + public void testSharedSecretProperlyConfigured() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.SECRET.name()); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET)).thenReturn(SECRET); + Mockito.when(globalProperties.getProperty(PROP_SHARED_SECRET_HEADER)).thenReturn(SHARED_SECRET_HEADER); + + filter.init(filterConfig); + } + + @Test (expected = AlfrescoRuntimeException.class) + public void testSecureCommsNoneAndNotAllowUnauthenticatedSolrEndpoint() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.NONE.name()); + Mockito.when(filterConfig.getInitParameter(ALLOW_UNAUTHORIZED_SOLR_ENDPOINT)).thenReturn("false"); + + try + { + filter.init(filterConfig); + } + catch (AlfrescoRuntimeException ex) + { + assertEquals(SECURE_COMMS_NONE_IS_NOT_SUPPORTED_EXCEPTION_MSG, ex.getMsgId()); + throw ex; + } + } + + @Test + public void testSecureCommsNoneAndAllowUnauthenticatedSolrEndpoint() throws ServletException + { + Mockito.when(globalProperties.getProperty(PROP_SECURE_COMMS)).thenReturn(SecureCommsType.NONE.name()); + Mockito.when(filterConfig.getInitParameter(ALLOW_UNAUTHORIZED_SOLR_ENDPOINT)).thenReturn("true"); + + filter.init(filterConfig); + } + +}