Java tutorial
/************************************************************************* * * * SignServer: The OpenSource Automated Signing Server * * * * This software 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 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.signserver.web; import java.io.IOException; import java.io.StringReader; import java.net.HttpURLConnection; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.bouncycastle.util.Arrays; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; import org.signserver.common.CryptoTokenAuthenticationFailureException; import org.signserver.common.CryptoTokenOfflineException; import org.signserver.common.InvalidWorkerIdException; import org.signserver.module.xmlvalidator.XMLValidatorTestData; import org.signserver.server.signers.EchoRequestMetadataSigner; import org.junit.Test; import org.signserver.common.GlobalConfiguration; import org.signserver.testutils.ModulesTestCase; /** * Tests that the right HTTP status codes are returned in different situations. * * @author Markus Kils * @version $Id: GenericProcessServletResponseTest.java 5856 2015-03-11 12:32:03Z netmackan $ */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class GenericProcessServletResponseTest extends WebTestCase { private static final String KEYDATA = "KEYDATA"; @Override protected String getServletURL() { return getPreferredHTTPProtocol() + getHTTPHost() + ":" + getPreferredHTTPPort() + "/signserver/process"; } /** * Sets up a dummy signer. * @throws Exception in case of error */ @Test public void test00SetupDatabase() throws Exception { addDummySigner1(false); addCMSSigner1(); addXMLValidator(); addSigner(EchoRequestMetadataSigner.class.getName(), 123, "DummySigner123", true); getWorkerSession().activateSigner(getSignerIdDummy1(), ModulesTestCase.KEYSTORE_PASSWORD); getWorkerSession().activateSigner(getSignerIdCMSSigner1(), ModulesTestCase.KEYSTORE_PASSWORD); } /** * Test that a successful request returns status code 200. */ @Test public void test01HttpStatus200() { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("data", "<root/>"); assertStatusReturned(fields, 200); } /** * Test that a bad request returns status code 400. * This request misses the "data" field. */ @Test public void test02HttpStatus400_missingField() { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); // Notice: No "data" field added assertStatusReturned(fields, 400); } /** * Test that a bad request returns status code 400. * This request contains an invalid XML document. */ @Test public void test02HttpStatus400_invalidDocument() { final String invalidXMLDoc = "<noEndTagToThis>"; Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", String.valueOf(getSignerIdDummy1())); fields.put("data", invalidXMLDoc); assertStatusReturned(fields, 400); } /** * Test that a bad request returns status code 400. * This request contains an unknown encoding property. */ @Test public void test02HttpStatus400_unknownEncoding() { final String unknownEncoding = "_unknownEncoding123_"; Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", String.valueOf(getSignerIdDummy1())); fields.put("data", "<root/>"); fields.put("encoding", unknownEncoding); // Run tests but skip the multipart/form-data as it does not use any // encoding property assertStatusReturned(fields, 400, true); } /** * Test that a request for non-existing worker returns status code 404. */ @Test public void test03HttpStatus404_nonExistingName() { final String nonExistingWorker = "_NotExistingWorker123_"; Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", nonExistingWorker); fields.put("data", "<root/>"); assertStatusReturned(fields, 404); } /** * Test that a request for non-existing worker returns status code 404. */ @Test public void test03HttpStatus404_nonExistingId() { final int nonExistingId = 0; Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", String.valueOf(nonExistingId)); fields.put("data", "<root/>"); assertStatusReturned(fields, 404); } /** * Test that when the cryptotoken is offline the status code is 503. */ @Test public void test04HttpStatus503() { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("data", "<root/>"); try { // Deactivate crypto token try { getWorkerSession().deactivateSigner(getSignerIdDummy1()); } catch (CryptoTokenOfflineException ex) { fail(ex.getMessage()); } catch (InvalidWorkerIdException ex) { fail(ex.getMessage()); } assertStatusReturned(fields, 503); } finally { // Activat crypto token try { getWorkerSession().activateSigner(getSignerIdDummy1(), ModulesTestCase.KEYSTORE_PASSWORD); } catch (CryptoTokenAuthenticationFailureException ex) { fail(ex.getMessage()); } catch (CryptoTokenOfflineException ex) { fail(ex.getMessage()); } catch (InvalidWorkerIdException ex) { fail(ex.getMessage()); } } } /** * Test that when an exception occurs status code 500 is returned. */ @Test public void test05HttpStatus500_exception() throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException, InvalidWorkerIdException { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("data", "<root/>"); // Set any bad properties that will make the signer fail with an exception final String originalSignatureAlgorithm = getWorkerSession().getCurrentWorkerConfig(getSignerIdDummy1()) .getProperty("SIGNATUREALGORITHM"); final String badKeyData = "_any-non-existing-alg_"; getWorkerSession().setWorkerProperty(getSignerIdDummy1(), "SIGNATUREALGORITHM", badKeyData); getWorkerSession().reloadConfiguration(getSignerIdDummy1()); getWorkerSession().activateSigner(getSignerIdDummy1(), ModulesTestCase.KEYSTORE_PASSWORD); try { assertStatusReturned(fields, 500); } finally { // Restore if (originalSignatureAlgorithm == null) { getWorkerSession().removeWorkerProperty(getSignerIdDummy1(), "SIGNATUREALGORITHM"); } else { getWorkerSession().setWorkerProperty(getSignerIdDummy1(), "SIGNATUREALGORITHM", originalSignatureAlgorithm); } getWorkerSession().reloadConfiguration(getSignerIdDummy1()); getWorkerSession().activateSigner(getSignerIdDummy1(), ModulesTestCase.KEYSTORE_PASSWORD); } } @Test public void test06AttachmentFileName() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameCMSSigner1()); fields.put("data", "Something to sign..."); final String expectedResponseFilename = "mydocument.dat.p7s"; final String expected = "attachment; filename=\"" + expectedResponseFilename + "\""; HttpURLConnection con = sendPostMultipartFormData(getServletURL(), fields, "mydocument.dat"); assertEquals(200, con.getResponseCode()); final String actual = con.getHeaderField("Content-Disposition"); assertEquals("Returned filename", expected, actual); con.disconnect(); } /** * Test explicitly setting the processType request parameter * to signDocument (the default value). * * @throws Exception */ @Test public void test07ExplicitProcessTypeSignDocument() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("processType", "signDocument"); fields.put("data", "<root/>"); assertStatusReturned(fields, 200); } /** * Test setting processType to validateDocument for a signer. * * @throws Exception */ @Test public void test08WrongProcessType() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("processType", "validateDocument"); fields.put("data", "<root/>"); assertStatusReturned(fields, 400); } /** * Test setting processType to signDocument for a validator. * * @throws Exception */ @Test public void test08WrongProcessTypeValidator() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getWorkerNameXmlValidator()); fields.put("processType", "signDocument"); fields.put("data", "<root/>"); assertStatusReturned(fields, 400); } /** * Test setting an invalid value for processType. * * @throws Exception */ @Test public void test10InvalidProcessType() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameDummy1()); fields.put("processType", "foobar"); fields.put("data", "<root/>"); assertStatusReturned(fields, 400); } /** * Test issuing a validateDocument call. * * @throws Exception */ @Test public void test11ValidateDocument() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", Integer.toString(getWorkerIdXmlValidator())); fields.put("processType", "validateDocument"); fields.put("data", XMLValidatorTestData.TESTXML1); final byte[] content = sendPostFormUrlencodedReadBody(getServletURL(), fields); assertEquals("Response content", "VALID", new String(content)); } /** * Test validating a document with an invalid signature. * * @throws Exception */ @Test public void test12ValidateDocumentInvalid() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", Integer.toString(getWorkerIdXmlValidator())); fields.put("processType", "validateDocument"); fields.put("data", XMLValidatorTestData.TESTXML2); final byte[] content = sendPostFormUrlencodedReadBody(getServletURL(), fields); assertEquals("Response content", "INVALID", new String(content)); } /** * Test validating a valid certificate using the validation service through the HTTP servlet. * * @throws Exception */ @Test public void test13ValidateCertificate() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", Integer.toString(getWorkerIdValidationService())); fields.put("processType", "validateCertificate"); fields.put("data", XMLValidatorTestData.CERT_ISSUER); fields.put("encoding", "base64"); // test returned status (GET, POST and POST with multi-part content) assertStatusReturned(fields, 200); // check the returned content final byte[] content = sendAndReadyBody(fields); assertEquals("Response content", "VALID;;This certificate is valid;-1;", new String(content)); } /** * Test validating an other, non-supported issuer using the validation service through the HTTP servlet. * * @throws Exception */ @Test public void test14ValidateCertificateOther() throws Exception { Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", Integer.toString(getWorkerIdValidationService())); fields.put("processType", "validateCertificate"); fields.put("data", XMLValidatorTestData.CERT_OTHER); fields.put("encoding", "base64"); // check the returned content final byte[] content = sendAndReadyBody(fields); assertEquals("Response content", "ISSUERNOTSUPPORTED;;Issuer of given certificate isn't supported;-1;", new String(content)); } private Properties parseMetadataResponse(final byte[] resp) throws IOException { final String propsString = new String(resp); final Properties props = new Properties(); props.load(new StringReader(propsString)); return props; } /** * Test setting a single REQUEST_METADATA.x param. * * @throws Exception */ @Test public void test15RequestMetadataSingleParam() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA.FOO", "BAR"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "BAR", props.getProperty("FOO")); } /** * Test passing in metdata parameters using the properties file syntax. * * @throws Exception */ @Test public void test16RequestMetadataPropertiesFile() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO=BAR\nFOO2=BAR2"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "BAR", props.getProperty("FOO")); assertEquals("Contains property", "BAR2", props.getProperty("FOO2")); } /** * Test passing in metdata parameters using the properties file syntax * and also override a single parameter. * * @throws Exception */ @Test public void test17RequestMetadataOverride() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO=BAR\nFOO2=BAR2"); fields.put("REQUEST_METADATA.FOO", "OVERRIDE"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "OVERRIDE", props.getProperty("FOO")); assertEquals("Contains property", "BAR2", props.getProperty("FOO2")); } /** * Test including properties with an escaped "=" sign as part of a property value. * @throws Exception */ @Test public void test18RequestMetadataEscaped() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO=FOO\\=BAR\nFOO2=BAR2"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "FOO=BAR", props.getProperty("FOO")); assertEquals("Contains property", "BAR2", props.getProperty("FOO2")); } /** * Test including a property value broken up on two lines with a line-ending \. * * @throws Exception */ @Test public void test19RequestMetadataLineEndingBackslash() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO=BAR\\\nNEXT_LINE\nFOO2=BAR2"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "BARNEXT_LINE", props.getProperty("FOO")); assertEquals("Contains property", "BAR2", props.getProperty("FOO2")); } /** * Test with a comment line in the property file. * * @throws Exception */ @Test public void test20RequestMetadataWithCommentLine() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO=BAR\n# Comment = a comment\nFOO2=BAR2"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); // Properties.load() seems to include some sort of marker as the first entry... assertEquals("Number of properties", 3, props.size()); } /** * Test with extra whitespace surrounding the "=". * * @throws Exception */ @Test public void test21RequestMetadataExtraWhitespace() throws Exception { final Map<String, String> fields = new HashMap<String, String>(); fields.put("workerId", "123"); fields.put("data", "foo"); fields.put("REQUEST_METADATA", "FOO = BAR\nFOO2 = BAR2"); assertStatusReturned(fields, 200); final byte[] resp = sendAndReadyBody(fields); final Properties props = parseMetadataResponse(resp); assertEquals("Contains property", "BAR", props.getProperty("FOO")); assertEquals("Contains property", "BAR2", props.getProperty("FOO2")); } /** * Tests that the maximum upload size can be configured and is enforced. * @throws Exception */ @Test public void test22MaxUploadSize() throws Exception { try { getGlobalSession().setProperty(GlobalConfiguration.SCOPE_GLOBAL, "HTTP_MAX_UPLOAD_SIZE", "700"); // 700 bytes max getGlobalSession().reload(); Map<String, String> fields = new HashMap<String, String>(); fields.put("workerName", getSignerNameCMSSigner1()); // Test with a small number of bytes // Note we can not test with 700 bytes as there is also headers that take up space byte[] data = new byte[10]; Arrays.fill(data, "a".getBytes("ASCII")[0]); fields.put("data", new String(data, "ASCII")); assertStatusReturned(fields, 200); // Test with more than 700 bytes upload data = new byte[701]; Arrays.fill(data, "a".getBytes("ASCII")[0]); fields.put("data", new String(data, "ASCII")); assertStatusReturned(fields, 413); } finally { getGlobalSession().removeProperty(GlobalConfiguration.SCOPE_GLOBAL, "HTTP_MAX_UPLOAD_SIZE"); getGlobalSession().reload(); } } /** * Remove the workers created etc. * @throws Exception in case of error */ @Test public void test99TearDownDatabase() throws Exception { removeWorker(getSignerIdDummy1()); removeWorker(getSignerIdCMSSigner1()); removeWorker(getWorkerIdXmlValidator()); removeWorker(getWorkerIdValidationService()); removeWorker(123); } }