diff options
Diffstat (limited to 'pdf-as-web')
7 files changed, 506 insertions, 3 deletions
| diff --git a/pdf-as-web/build.gradle b/pdf-as-web/build.gradle index 49310e2f..a6ca37aa 100644 --- a/pdf-as-web/build.gradle +++ b/pdf-as-web/build.gradle @@ -51,7 +51,8 @@ dependencies {  	compile 'com.thetransactioncompany:cors-filter:2.3'  	compile 'ch.qos.logback:logback-classic:1.1.3'  	compile 'ch.qos.logback:logback-core:1.1.3' -	 +	compile 'org.json:json:20160212' +  	providedCompile 'javax.servlet:javax.servlet-api:3.0.1'      testCompile group: 'junit', name: 'junit', version: '4.+'  } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java index 4ef320a1..7fbed8d9 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java @@ -57,6 +57,7 @@ public class WebConfiguration implements IConfigurationConstants {  	public static final String SOAP_VERIFY_ENABLED = "soap.verify.enabled";  	public static final String RELOAD_PASSWORD = "reload.pwd";  	public static final String RELOAD_ENABLED = "reload.enabled"; +	public static final String KEEP_SIGNED_DOCUMENT = "keep.signed";  	public static final String MOA_LIST = "moal";  	public static final String MOA_URL = "url"; @@ -299,7 +300,17 @@ public class WebConfiguration implements IConfigurationConstants {  		}  		return false;  	} -	 + +	public static boolean isKeepSignedDocument() { +		String value = properties.getProperty(KEEP_SIGNED_DOCUMENT); +		if (value != null) { +			if (value.equals("true")) { +				return true; +			} +		} +		return false; +	} +  	public static boolean isMoaEnabled(String keyIdentifier) {  		String value = properties.getProperty(MOA_LIST + "." + keyIdentifier + ".enabled");  		if (value != null) { diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/JSONStartResponse.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/JSONStartResponse.java new file mode 100644 index 00000000..98c59981 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/JSONStartResponse.java @@ -0,0 +1,59 @@ +package at.gv.egiz.pdfas.web.helper; + +import at.gv.egiz.sl.util.SLMarschaller; + +/** + * Created by Andreas Fitzek on 6/23/16. + */ +public class JSONStartResponse { + +    String url; +    String slRequest; +    String template; +    String locale; +    String bkuURL; + +    public JSONStartResponse(String url, String slRequest, String template, String locale, String bkuURL) { +        this.url = url; +        this.slRequest = slRequest; +        this.template = template; +        this.bkuURL = bkuURL; +        this.locale = locale; +    } + +    public String getBkuURL() { +        return bkuURL; +    } + +    public String getUrl() { +        return url; +    } + +    public void setUrl(String url) { +        this.url = url; +    } + +    public String getLocale() { +        return locale; +    } + +    public void setLocale(String locale) { +        this.locale = locale; +    } + +    public String getTemplate() { +        return template; +    } + +    public void setTemplate(String template) { +        this.template = template; +    } + +    public String getSlRequest() { +        return slRequest; +    } + +    public void setSlRequest(String slRequest) { +        this.slRequest = slRequest; +    } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java index c4db2e4f..691ab423 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java @@ -97,6 +97,7 @@ public class PdfAsHelper {  	private static final String PDF_SIGNER = "PDF_SIGNER";  	private static final String PDF_SL_INTERACTIVE = "PDF_SL_INTERACTIVE";  	private static final String PDF_SIGNED_DATA = "PDF_SIGNED_DATA"; +	private static final String PDF_SIGNED_DATA_CREATED = "PDF_SIGNED_DATA_CREATED";  	private static final String PDF_LOCALE = "PDF_LOCALE";  	private static final String PDF_ERR_MESSAGE = "PDF_ERR_MESSAGE";  	private static final String PDF_ERR_THROWABLE = "PDF_ERR_THROWABLE"; @@ -664,6 +665,93 @@ public class PdfAsHelper {  		return signResponse;  	} +	public static void startSignatureJson(HttpServletRequest request, +									  HttpServletResponse response, ServletContext context, +									  byte[] pdfData, String connector, String position, +									  String transactionId, String profile, +									  Map<String, String> preProcessor, Map<String, String> overwrite) throws Exception { + +		// TODO: Protect session so that only one PDF can be signed during one +		// session +		/* +		 * if(PdfAsHelper.isSignatureActive(request)) { throw new +		 * PdfAsException("Signature is active in this session"); } +		 * +		 * PdfAsHelper.setSignatureActive(request, true); +		 */ + +		validatePdfSize(request, response, pdfData); + +		HttpSession session = request.getSession(); + +		logger.info("Starting signature in session: " + session.getId()); + +		Configuration config = pdfAs.getConfiguration(); +		session.setAttribute(PDF_CONFIG, config); + +		ConfigurationOverwrite.overwriteConfiguration(overwrite, config); + +		ByteArrayOutputStream baos = new ByteArrayOutputStream(); +		session.setAttribute(PDF_OUTPUT, baos); + +		// Generate Sign Parameter +		SignParameter signParameter = PdfAsFactory.createSignParameter(config, +				new ByteArrayDataSource(pdfData), baos); + +		logger.info("Setting TransactionID: " + transactionId); + +		signParameter.setTransactionId(transactionId); + +		IPlainSigner signer; +		if (connector.equals("bku") || connector.equals("onlinebku") +				|| connector.equals("mobilebku")) { +			BKUSLConnector conn = new BKUSLConnector(config); +			// conn.setBase64(true); +			signer = new PAdESSigner(conn); +			session.setAttribute(PDF_SL_CONNECTOR, conn); +		} else { +			throw new PdfAsWebException( +					"Invalid connector (bku | onlinebku | mobilebku | moa | jks)"); +		} +		signParameter.setPreprocessorArguments(preProcessor); +		signParameter.setPlainSigner(signer); +		session.setAttribute(PDF_SIGNER, signer); +		session.setAttribute(PDF_SL_INTERACTIVE, connector); + +		String qrCodeContent = PdfAsHelper.getQRCodeContent(request); + +		if (qrCodeContent != null) { +			if (profile == null) { +				// get default Profile +				profile = config.getValue("sig_obj.type.default"); +			} + +			if (profile == null) { +				logger.warn("Failed to determine default profile! Using hard coded!"); +				profile = "SIGNATURBLOCK_SMALL_DE"; +			} + +			ByteArrayOutputStream qrbaos = new ByteArrayOutputStream(); +			try { +				String key = "sig_obj." + profile + ".value.SIG_LABEL"; +				QRCodeGenerator.generateQRCode(qrCodeContent, qrbaos, 200); +				String value = Base64.encodeBase64String(qrbaos.toByteArray()); +				config.setValue(key, value); +			} finally { +				IOUtils.closeQuietly(qrbaos); +			} +		} + +		// set Signature Profile (null use default ...) +		signParameter.setSignatureProfileId(profile); + +		// set Signature Position +		signParameter.setSignaturePosition(position); + +		StatusRequest statusRequest = pdfAs.startSign(signParameter); +		session.setAttribute(PDF_STATUS, statusRequest); +	} +  	public static void startSignature(HttpServletRequest request,  			HttpServletResponse response, ServletContext context,  			byte[] pdfData, String connector, String position, @@ -861,6 +949,43 @@ public class PdfAsHelper {  				+ session.getId());  	} +	public static JSONStartResponse startJsonProcess(HttpServletRequest request, +										HttpServletResponse response, ServletContext context) +			throws Exception { +		HttpSession session = request.getSession(); +		StatusRequest statusRequest = (StatusRequest) session +				.getAttribute(PDF_STATUS); +		// IPlainSigner plainSigner = (IPlainSigner) session +		// .getAttribute(PDF_SIGNER); + +		String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE); + +		if (connector.equals("bku") || connector.equals("onlinebku") +				|| connector.equals("mobilebku")) { +			BKUSLConnector bkuSLConnector = (BKUSLConnector) session +					.getAttribute(PDF_SL_CONNECTOR); + +			if (statusRequest.needCertificate()) { +				logger.debug("Needing Certificate from BKU"); +				// build SL Request to read certificate +				InfoboxReadRequestType readCertificateRequest = bkuSLConnector +						.createInfoboxReadRequest(statusRequest +								.getSignParameter()); + +				JAXBElement<InfoboxReadRequestType> readRequest = of +						.createInfoboxReadRequest(readCertificateRequest); + +				String url = generateDataURL(request, response); +				String slRequest = SLMarschaller.marshalToString(readRequest); +				String template = getTemplateSL(); +				String locale = getLocale(request, response); +				String bkuURL = generateBKUURL(connector); +				return new JSONStartResponse(url, slRequest, template, locale, bkuURL); +			} +		} +		return null; +	} +  	public static void process(HttpServletRequest request,  			HttpServletResponse response, ServletContext context)  			throws Exception { @@ -1018,6 +1143,26 @@ public class PdfAsHelper {  		return xml;  	} +	public static boolean isSignedDataExpired(HttpServletRequest request, +											  HttpServletResponse response) { +		HttpSession session = request.getSession(); +		Object signedData = session.getAttribute(PDF_SIGNED_DATA_CREATED); +		if (signedData == null) { +			return true; +		} + +		if (signedData instanceof Long) { +			long created = ((Long)signedData).longValue(); +			long now = System.currentTimeMillis(); + +			long validUntil = created + 300000; + +			return validUntil > now; +		} +		logger.warn("PDF_SIGNED_DATA_CREATED in session is not a long type!"); +		return true; +	} +  	public static byte[] getSignedPdf(HttpServletRequest request,  			HttpServletResponse response) {  		HttpSession session = request.getSession(); @@ -1037,6 +1182,7 @@ public class PdfAsHelper {  			HttpServletResponse response, byte[] signedData) {  		HttpSession session = request.getSession();  		session.setAttribute(PDF_SIGNED_DATA, signedData); +		session.setAttribute(PDF_SIGNED_DATA_CREATED, Long.valueOf(System.currentTimeMillis()));  	}  	public static void setStatisticEvent(HttpServletRequest request, diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java new file mode 100644 index 00000000..1b9b4560 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java @@ -0,0 +1,255 @@ +package at.gv.egiz.pdfas.web.servlets; + +import at.gv.egiz.pdfas.api.ws.PDFASSignParameters; +import at.gv.egiz.pdfas.api.ws.PDFASSignRequest; +import at.gv.egiz.pdfas.api.ws.PDFASSignResponse; +import at.gv.egiz.pdfas.api.ws.VerificationLevel; +import at.gv.egiz.pdfas.common.exceptions.PDFASError; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; +import at.gv.egiz.pdfas.web.config.WebConfiguration; +import at.gv.egiz.pdfas.web.exception.PdfAsWebException; +import at.gv.egiz.pdfas.web.filter.UserAgentFilter; +import at.gv.egiz.pdfas.web.helper.DigestHelper; +import at.gv.egiz.pdfas.web.helper.JSONStartResponse; +import at.gv.egiz.pdfas.web.helper.PdfAsHelper; +import at.gv.egiz.pdfas.web.stats.StatisticEvent; +import at.gv.egiz.pdfas.web.stats.StatisticFrontend; +import at.gv.egiz.pdfas.web.store.RequestStore; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.json.HTTP; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.ws.WebServiceException; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Created by Andreas Fitzek on 6/23/16. + */ +public class JSONAPIServlet extends HttpServlet { + +    private static final String JSON_PROFILE = "profile"; +    private static final String JSON_POSITION = "position"; +    private static final String JSON_CONNECTOR = "connector"; +    private static final String JSON_REQUEST_ID = "reqID"; +    private static final String JSON_INPUT = "input"; +    private static final String JSON_OUTPUT = "output"; +    private static final String JSON_OUTPUT_SIG = "verifySignature"; +    private static final String JSON_OUTPUT_CER = "verifyCertificate"; +    private static final String JSON_DATAURL = "dataUrl"; +    private static final String JSON_BKUURL = "bkuUrl"; +    private static final String JSON_SLREQUEST = "slRequest"; + +    private static final Logger logger = LoggerFactory.getLogger(JSONAPIServlet.class); + +    @Override +    protected void doPost(HttpServletRequest req, HttpServletResponse resp) +            throws ServletException, IOException { + +        String jsonString = IOUtils.toString(req.getInputStream(), "UTF-8"); + +        logger.debug("Reading json String {}", jsonString); + +        JSONObject jsonObject = new JSONObject(jsonString); + +        logger.debug("JSON parsed: {}", jsonObject.toString()); + +        process(req, resp, jsonObject); +    } + +    protected void process(HttpServletRequest request, +                           HttpServletResponse response, +                           JSONObject jsonObject) throws ServletException, IOException { + +        JSONObject jsonResponse = new JSONObject(); + + + +        String profile = jsonObject.has(JSON_PROFILE) ? jsonObject.getString(JSON_PROFILE) : null; +        String position = jsonObject.has(JSON_POSITION) ? jsonObject.getString(JSON_POSITION) : null; +        String connector = jsonObject.getString(JSON_CONNECTOR); +        String input = jsonObject.getString(JSON_INPUT); +        String requestID = jsonObject.has(JSON_REQUEST_ID) ? jsonObject.getString(JSON_REQUEST_ID) : null; + +        if(input == null) { +            throw new ServletException( +                    "Invalid input value!"); +        } + +        byte[] inputDocument = Base64.decodeBase64(input); + +        StatisticEvent statisticEvent = new StatisticEvent(); +        statisticEvent.setSource(StatisticEvent.Source.JSON); +        statisticEvent.setOperation(StatisticEvent.Operation.SIGN); +        statisticEvent.setUserAgent(UserAgentFilter.getUserAgent()); +        statisticEvent.setStartNow(); + +        try { + +            if(connector == null) { +                throw new ServletException( +                        "Invalid connector value!"); +            } + +            PDFASSignParameters.Connector connectorEnum = null; + +            if(PDFASSignParameters.Connector.MOA.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.MOA; +            } else if(PDFASSignParameters.Connector.JKS.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.JKS; +            } else if(PDFASSignParameters.Connector.BKU.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.BKU; +            } else if(PDFASSignParameters.Connector.MOBILEBKU.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.MOBILEBKU; +            } else if(PDFASSignParameters.Connector.ONLINEBKU.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.ONLINEBKU; +            } + +            if(connectorEnum == null) { +                throw new ServletException( +                        "Invalid connector value!"); +            } + +            // TODO: check connector is enabled! + +            statisticEvent.setFilesize(inputDocument.length); +            statisticEvent.setProfileId(profile); +            statisticEvent.setDevice(connector); + +            PDFASSignParameters parameters = new PDFASSignParameters(); +            parameters.setConnector(connectorEnum); +            parameters.setPosition(position); +            parameters.setProfile(profile); + +            if (PDFASSignParameters.Connector.MOA.equals(connectorEnum) +                    || PDFASSignParameters.Connector.JKS.equals(connectorEnum)) { +                // Plain server based signatures!! +                PDFASSignResponse pdfasSignResponse = PdfAsHelper.synchornousServerSignature( +                        inputDocument, parameters); + +                VerifyResult verifyResult = null; + +                List<VerifyResult> verResults = PdfAsHelper +                           .synchornousVerify( +                                    pdfasSignResponse.getSignedPDF(), +                                    -1, +                                    VerifyParameter.SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION, +                                    null); + +                if (verResults.size() != 1) { +                   throw new ServletException( +                          "Document verification failed!"); +                } +                verifyResult = verResults.get(0); + +                if(verifyResult.getValueCheckCode().getCode() == 0) { +                    statisticEvent.setStatus(StatisticEvent.Status.OK); +                    statisticEvent.setEndNow(); +                    statisticEvent.setTimestampNow(); +                    StatisticFrontend.getInstance().storeEvent(statisticEvent); +                    statisticEvent.setLogged(true); +                } else { +                    statisticEvent.setStatus(StatisticEvent.Status.ERROR); +                    statisticEvent.setErrorCode(verifyResult.getValueCheckCode().getCode()); +                    statisticEvent.setEndNow(); +                    statisticEvent.setTimestampNow(); +                    StatisticFrontend.getInstance().storeEvent(statisticEvent); +                    statisticEvent.setLogged(true); +                } + +                jsonResponse.put(JSON_OUTPUT, Base64.encodeBase64String(pdfasSignResponse.getSignedPDF())); +                jsonResponse.put(JSON_OUTPUT_SIG, verifyResult.getValueCheckCode().getCode()); +                jsonResponse.put(JSON_OUTPUT_CER, verifyResult.getCertificateCheck().getCode()); + +            } else { + +                PdfAsHelper.setStatisticEvent(request, response, statisticEvent); +                PdfAsHelper.setVerificationLevel(request, +                        VerifyParameter.SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION); + +                String pdfDataHash = DigestHelper.getHexEncodedHash(inputDocument); + +                PdfAsHelper.setSignatureDataHash(request, pdfDataHash); +                logger.debug("Storing signatures data hash: " + pdfDataHash); + +                logger.debug("Starting signature creation with: " + connector); + +                // start asynchronous signature creation + +                    if (PDFASSignParameters.Connector.BKU.equals(connectorEnum)) { +                        if (WebConfiguration.getLocalBKUURL() == null) { +                            throw new PdfAsWebException( +                                    "Invalid connector bku is not supported"); +                        } +                    } + +                    if (PDFASSignParameters.Connector.ONLINEBKU.equals(connectorEnum)) { +                        if (WebConfiguration.getLocalBKUURL() == null) { +                            throw new PdfAsWebException( +                                    "Invalid connector onlinebku is not supported"); +                        } +                    } + +                    if (PDFASSignParameters.Connector.MOBILEBKU.equals(connectorEnum)) { +                        if (WebConfiguration.getLocalBKUURL() == null) { +                            throw new PdfAsWebException( +                                    "Invalid connector mobilebku is not supported"); +                        } +                    } + + +                    PdfAsHelper.startSignatureJson(request, response, getServletContext(), +                            inputDocument, connectorEnum.toString(), +                            position, +                            null, +                            profile, null, +                            null); + +                JSONStartResponse jsonStartResponse = PdfAsHelper.startJsonProcess(request, response, getServletContext()); + +                if(jsonStartResponse == null) { +                    throw new PdfAsWebException( +                            "Invalid configuration for json API"); +                } + +                jsonResponse.put(JSON_DATAURL, jsonStartResponse.getUrl()); +                jsonResponse.put(JSON_BKUURL, jsonStartResponse.getBkuURL()); +                jsonResponse.put(JSON_SLREQUEST, jsonStartResponse.getSlRequest()); +            } + +            response.setContentType("application/json"); +            IOUtils.write(jsonResponse.toString(), response.getOutputStream(), "UTF-8"); + +        } catch (Throwable e) { + +            statisticEvent.setStatus(StatisticEvent.Status.ERROR); +            statisticEvent.setException(e); +            if(e instanceof PDFASError) { +                statisticEvent.setErrorCode(((PDFASError)e).getCode()); +            } +            statisticEvent.setEndNow(); +            statisticEvent.setTimestampNow(); +            StatisticFrontend.getInstance().storeEvent(statisticEvent); +            statisticEvent.setLogged(true); + +            logger.warn("Error in JSON Service", e); +            if (e.getCause() != null) { +                throw new ServletException(e.getCause().getMessage()); +            } else { +                throw new ServletException(e.getMessage()); +            } +        } finally { +            logger.debug("Done JSON Sign Request"); +        } +    } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java index 4fce6860..cf8486b5 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java @@ -31,6 +31,7 @@ import javax.servlet.http.HttpServlet;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse; +import at.gv.egiz.pdfas.web.config.WebConfiguration;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; @@ -85,6 +86,18 @@ public class PDFData extends HttpServlet {  		String plainPDFDigest = PdfAsParameterExtractor.getOrigDigest(request);  		if (signedData != null) { + +			if(WebConfiguration.isKeepSignedDocument()) { +				if(PdfAsHelper.isSignedDataExpired(request, response)) { +					logger.debug("Destroying expired signed data in session : {}", request.getSession().getId()); +					request.getSession().invalidate(); +					PdfAsHelper.setSessionException(request, response, +							"No signed pdf document available.", null); +					PdfAsHelper.gotoError(getServletContext(), request, response); +					return; +				} +			} +  			if (plainPDFDigest != null) {  				String signatureDataHash = PdfAsHelper  						.getSignatureDataHash(request); @@ -132,7 +145,12 @@ public class PDFData extends HttpServlet {  			os.close();  			// When data is collected destroy session! -			request.getSession().invalidate(); +			if(!WebConfiguration.isKeepSignedDocument()) { +				logger.debug("Destroying signed data in session : {}", request.getSession().getId()); +				request.getSession().invalidate(); +			} else { +				logger.debug("Keeping signed data in session : {}", request.getSession().getId()); +			}  		} else {  			PdfAsHelper.setSessionException(request, response,  					"No signed pdf document available.", null); diff --git a/pdf-as-web/src/main/webapp/WEB-INF/web.xml b/pdf-as-web/src/main/webapp/WEB-INF/web.xml index 3e6b30bd..a7bb5f74 100644 --- a/pdf-as-web/src/main/webapp/WEB-INF/web.xml +++ b/pdf-as-web/src/main/webapp/WEB-INF/web.xml @@ -139,6 +139,13 @@  		<description></description>  		<servlet-class>at.gv.egiz.pdfas.web.servlets.PlaceholderGeneratorServlet</servlet-class>  	</servlet> +	<servlet> +		<servlet-name>JSONAPIServlet</servlet-name> +		<display-name>JSONAPIServlet</display-name> +		<description></description> +		<servlet-class>at.gv.egiz.pdfas.web.servlets.JSONAPIServlet</servlet-class> +	</servlet> +  	<!-- Define mappings that are used by the servlet container to translate   		a particular request URI (context-relative) to a particular servlet. The  @@ -206,6 +213,12 @@  		<url-pattern>/placeholder</url-pattern>  	</servlet-mapping> +	<servlet-mapping> +		<servlet-name>JSONAPIServlet</servlet-name> +		<url-pattern>/api/v1/sign</url-pattern> +	</servlet-mapping> + +  	<!-- Define the default session timeout for your application, in minutes.   		From a servlet or JSP page, you can modify the timeout for a particular session   		dynamically by using HttpSession.getMaxInactiveInterval(). --> | 
