Merged HEAD-BUG-FIX (5.1/Cloud) to HEAD (5.1/Cloud)

93371: Merged 5.0.N (5.0.1) to HEAD-BUG-FIX (5.1/Cloud)
      93309: MNT-12794: Merged dev. 5.0.N (5.0.1) to 5.0.N (5.0.1)
          92772: MNT-12794: [Security] Information leak via verbose eror messages
              - Not public exceptions have been transformed into a configurable parameter. The basic configuration has been moved to .web-scripts-application-context.xml.. A safe choice for the name of the parent bean in the context of enterprise tests was put into the .enterprise-web-scripts-application-context.xml.. to ensure that the Cloud tests will work well without not public exceptions configuration. All tests related to the not public exceptions are implemented in .org.alfresco.repo.web.scripts.RepositoryContainerTest.. << N. B.: This revision does not contain changes for Cloud >>


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@94956 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2015-01-31 12:21:40 +00:00
parent 6d5691a0b0
commit 586807e319
4 changed files with 288 additions and 88 deletions

View File

@@ -44,7 +44,7 @@
<property name="retryingTransactionHelper" ref="retryingTransactionHelper" />
</bean>
<bean id="publicapi.container" class="org.alfresco.rest.api.PublicApiRepositoryContainer" parent="webscripts.abstractcontainer" init-method="setup">
<bean id="publicapi.container" class="org.alfresco.rest.api.PublicApiRepositoryContainer" parent="baseAlfrescoRepositoryContainer" init-method="setup">
<property name="name"><value>Public Api</value></property>
<property name="webScriptsRegistryCache" ref="publicapi.webScriptsRegistryCache" />
<!-- Use the time-limited transaction helper to keep request times to an acceptable duration -->

View File

@@ -134,7 +134,16 @@
<property name="extension"><value>js</value></property>
</bean>
<bean id="webscripts.container" class="org.alfresco.repo.web.scripts.TenantRepositoryContainer" parent="webscripts.abstractcontainer" init-method="setup">
<bean id="baseAlfrescoRepositoryContainer" parent="webscripts.abstractcontainer" abstract="true">
<property name="notPublicExceptions">
<list>
<value>java.sql.SQLException</value>
<value>org.alfresco.service.cmr.repository.ContentIOException</value>
</list>
</property>
</bean>
<bean id="webscripts.container" class="org.alfresco.repo.web.scripts.TenantRepositoryContainer" parent="baseAlfrescoRepositoryContainer" init-method="setup">
<property name="configService" ref="webscripts.config" />
<property name="name"><value>Repository</value></property>
<property name="scriptObjects">

View File

@@ -21,8 +21,8 @@ package org.alfresco.repo.web.scripts;
import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
@@ -90,7 +90,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
private long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
private ThresholdOutputStreamFactory streamFactory = null;
private final static Class<?>[] HIDE_EXCEPTIONS = new Class[] { SQLException.class };
private Class<?>[] notPublicExceptions = new Class<?>[] {};
/*
* Shame init is already used (by TenantRepositoryContainer).
@@ -178,6 +178,25 @@ public class RepositoryContainer extends AbstractRuntimeContainer
this.authorityService = authorityService;
}
/**
* Exceptions which may contain information that cannot be displayed in UI
*
* @param notPublicExceptions - {@link Class}&lt;?&gt;[] instance which contains list of not public exceptions
*/
public void setNotPublicExceptions(List<Class<?>> notPublicExceptions)
{
this.notPublicExceptions = new Class<?>[] {};
if((null != notPublicExceptions) && !notPublicExceptions.isEmpty())
{
this.notPublicExceptions = notPublicExceptions.toArray(this.notPublicExceptions);
}
}
public Class<?>[] getNotPublicExceptions()
{
return notPublicExceptions;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.Container#getDescription()
*/
@@ -262,7 +281,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
}
catch (RuntimeException e)
{
Throwable hideCause = ExceptionStackUtil.getCause(e, HIDE_EXCEPTIONS);
Throwable hideCause = ExceptionStackUtil.getCause(e, notPublicExceptions);
if (hideCause != null)
{
AlfrescoRuntimeException alf = null;

View File

@@ -18,9 +18,15 @@
*/
package org.alfresco.repo.web.scripts;
import static org.mockito.Matchers.any;
import static org.springframework.extensions.webscripts.Status.STATUS_OK;
import java.sql.SQLException;
import java.util.regex.Pattern;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
@@ -30,6 +36,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.util.CronTriggerBean;
@@ -37,18 +44,14 @@ import org.alfresco.util.PropertyMap;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.Authenticator;
import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
import org.springframework.extensions.webscripts.Authenticator;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import static org.springframework.extensions.webscripts.Status.*;
import static org.mockito.Matchers.any;
/**
* Unit test to test runas function
*
@@ -56,6 +59,8 @@ import static org.mockito.Matchers.any;
*/
public class RepositoryContainerTest extends BaseWebScriptTest
{
private static final Pattern HIDDEN_EXCEPTION_PATTERN = Pattern.compile("Server error \\(\\d{8}\\)\\. Details can be found in the server logs\\.");
private MutableAuthenticationService authenticationService;
private PersonService personService;
private AuthenticationComponent authenticationComponent;
@@ -162,16 +167,22 @@ public class RepositoryContainerTest extends BaseWebScriptTest
assertEquals(SUCCESS, response.getContentAsString());
}
public void testHideExceptions() throws Exception
{
final Pattern patternHiddenException = Pattern.compile("Server error \\(\\d{8}\\)\\. Details can be found in the server logs\\.");
final String messageFormException = "Failed to persist field 'prop_cm_name'";
final String messageAuthenticationException = "Authentication failed for Web Script";
RepositoryContainer repoContainer = (RepositoryContainer) getServer().getApplicationContext().getBean("webscripts.container");
RepositoryContainer repoContainerMock = Mockito.spy(repoContainer);
Class<?>[] defaultConfiguration = repoContainerMock.getNotPublicExceptions();
try
{
List<Class<?>> testExceptoins = new LinkedList<Class<?>>();
testExceptoins.add(SQLException.class);
repoContainerMock.setNotPublicExceptions(testExceptoins);
// case: AlfrescoRuntimeException with SQLException cause
Mockito.doAnswer(new Answer<Object>()
{
@@ -187,7 +198,7 @@ public class RepositoryContainerTest extends BaseWebScriptTest
catch (Exception e)
{
assertNull("SQLException cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { SQLException.class }));
assertTrue("Details should be in the server logs.", patternHiddenException.matcher(e.getMessage()).matches());
assertTrue("Details should be in the server logs.", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: AlfrescoRuntimeException with NOT SQLException cause
@@ -205,7 +216,7 @@ public class RepositoryContainerTest extends BaseWebScriptTest
catch (Exception e)
{
assertNotNull("NullPointerException cause should be visible for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: RuntimeException with SQLException cause
@@ -223,7 +234,7 @@ public class RepositoryContainerTest extends BaseWebScriptTest
catch (Exception e)
{
assertNull("SQLException cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { SQLException.class }));
assertTrue("Details should be in the server logs.", patternHiddenException.matcher(e.getMessage()).matches());
assertTrue("Details should be in the server logs.", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: FormException
@@ -241,7 +252,7 @@ public class RepositoryContainerTest extends BaseWebScriptTest
catch (Exception e)
{
assertTrue("FormException should be visible for client", e instanceof FormException);
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
assertTrue("Actual message should be available for client", e.getMessage().contains(messageFormException));
}
@@ -260,8 +271,169 @@ public class RepositoryContainerTest extends BaseWebScriptTest
catch (Exception e)
{
assertTrue("WebScriptException should be visible for client", e instanceof WebScriptException);
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
assertTrue("Actual message should be available for client", e.getMessage().contains(messageAuthenticationException));
}
}
finally
{
repoContainerMock.setNotPublicExceptions(Arrays.asList(defaultConfiguration));
}
}
public void testHideExceptionsConfiguration() throws Exception
{
final StringBuilder commandExecutionResult = new StringBuilder(128);
commandExecutionResult.append("Execution result: \n").append(" os: ").append(System.getProperty("os.name")).append("\n").append(" command: <TEST_COMMAND>")
.append(" succeeded: ").append(false).append("\n").append(" exit code: ").append(1).append("\n").append(" out: ").append("<EMPTY>").append("\n")
.append(" err: ").append("ERROR");
RepositoryContainer repoContainer = (RepositoryContainer) getServer().getApplicationContext().getBean("webscripts.container");
RepositoryContainer repoContainerMock = Mockito.spy(repoContainer);
Class<?>[] notPublicExceptions = repoContainerMock.getNotPublicExceptions();
List<Class<?>> defaultConfiguration = Arrays.asList(notPublicExceptions);
List<Class<?>> testExceptions = new LinkedList<Class<?>>();
testExceptions.add(SQLException.class);
testExceptions.add(ContentIOException.class);
repoContainerMock.setNotPublicExceptions(testExceptions);
try
{
// Case: MNT-12794 - test default configuration
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new ContentIOException(commandExecutionResult.toString());
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNull("'ContentIOException' cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { ContentIOException.class }));
assertTrue("Details should be in the server logs.", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// Case: MNT-12794 - ContentIOException in AlfrescoRuntimeException and default configuration
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new ContentIOException(commandExecutionResult.toString()));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNull("'ContentIOException' cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { ContentIOException.class }));
assertTrue("Details should be in the server logs.", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: AlfrescoRuntimeException without ContentIOException
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new NullPointerException("NullPointerException"));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNotNull("'NullPointerException' cause should be visible for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: RuntimeException with several different exceptions which should be shown
List<Class<?>> updatedNotPublicExceptions = new LinkedList<Class<?>>(defaultConfiguration);
updatedNotPublicExceptions.add(NullPointerException.class);
repoContainerMock.setNotPublicExceptions(updatedNotPublicExceptions);
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new NullPointerException("NullPointerException"));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNull("'NullPointerException' cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertTrue("Details should be in the server logs.", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: all exceptions must be shown
repoContainerMock.setNotPublicExceptions(null);
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new ContentIOException(commandExecutionResult.toString(), new NullPointerException(
"NullPointerException")));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertTrue("'AlfrescoRuntimeException' cause should not be hidden for client. Exception class: " + e.getClass().getName(), e instanceof AlfrescoRuntimeException);
assertNotNull("'ContentIOException' cause should not be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { ContentIOException.class }));
assertNotNull("'NullPointerException' cause should not be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
// case: all exceptions must be shown
repoContainerMock.setNotPublicExceptions(defaultConfiguration);
repoContainerMock.setNotPublicExceptions(new LinkedList<Class<?>>());
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new ContentIOException(commandExecutionResult.toString(), new NullPointerException(
"NullPointerException")));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertTrue("'AlfrescoRuntimeException' cause should not be hidden for client. Exception class: " + e.getClass().getName(), e instanceof AlfrescoRuntimeException);
assertNotNull("'ContentIOException' cause should not be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { ContentIOException.class }));
assertNotNull("'NullPointerException' cause should not be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertFalse("Details should be available for client", HIDDEN_EXCEPTION_PATTERN.matcher(e.getMessage()).matches());
}
}
finally
{
repoContainerMock.setNotPublicExceptions(defaultConfiguration);
}
}
}