Skip to content

Commit

Permalink
Deprecate javax.servlet dependency (#459)
Browse files Browse the repository at this point in the history
* Deprecate javax.servlet dependency

* Removed javax.xml.bind dependency

* Bumped gradle wrapper version
  • Loading branch information
SMadani authored May 18, 2023
1 parent 601454d commit 4c31dc5
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 46 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added Verify v2 API implementation
- Added Advanced Machine Detection to Voice API
- Fixed VbcEndpoint NCCO
- Removed dependency on `jakarta.xml.bind`
- Made `jakarta.servlet` an optional dependency
- Deprecated all methods and classes that use `javax.servlet.HttpServletRequest`

# [7.3.0] - 2023-04-14
- Viber video message now requires setting duration and file size
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ repositories {
}

dependencies {
compileOnly 'jakarta.servlet:jakarta.servlet-api:4.0.4'

implementation 'commons-codec:commons-codec:1.15'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.0'
implementation 'io.openapitools.jackson.dataformat:jackson-dataformat-hal:1.0.9'
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3'
implementation 'jakarta.servlet:jakarta.servlet-api:4.0.4'
implementation 'com.vonage:jwt:1.0.2'

testImplementation 'junit:junit:4.13.2'
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
54 changes: 40 additions & 14 deletions src/main/java/com/vonage/client/auth/RequestSigning.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.http.message.BasicNameValuePair;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
Expand Down Expand Up @@ -154,9 +155,12 @@ protected static void constructSignatureForRequestParameters(List<NameValuePair>
* @param secretKey The pre-shared secret key used by the sender of the request to create the signature.
*
* @return true if the signature is correct for this request and secret key.
*
* @deprecated This method will be removed in a future release due to the dependency on javax.servlet.
*/
@Deprecated
public static boolean verifyRequestSignature(HttpServletRequest request, String secretKey) {
return verifyRequestSignature(request, secretKey, System.currentTimeMillis());
return verifyRequestSignature(request, secretKey, HashUtil.HashType.MD5);
}

/**
Expand All @@ -167,48 +171,70 @@ public static boolean verifyRequestSignature(HttpServletRequest request, String
* @param hashType Hash type to be used to construct request parameters.
*
* @return true if the signature is correct for this request and secret key.
*
* @deprecated This method will be removed in a future release due to the dependency on javax.servlet.
*/
@Deprecated
public static boolean verifyRequestSignature(HttpServletRequest request, String secretKey, HashUtil.HashType hashType) {
return verifyRequestSignature(request, secretKey, System.currentTimeMillis(), hashType);
try {
return verifyRequestSignature(
request.getContentType(),
request.getInputStream(),
request.getParameterMap(),
secretKey, System.currentTimeMillis(), hashType
);
}
catch (IOException ex) {
throw new VonageUnexpectedException("Error encountered when opening input stream for request", ex);
}
}

/**
* Verifies the signature in an HttpServletRequest.
* Hashing strategy is MD5.
* Verifies the signature in an HttpServletRequest. Hashing strategy is MD5.
*
* @param request The HttpServletRequest to be verified.
* @param contentType The request Content-Type header.
* @param inputStream The request data stream.
* @param parameterMap The request parameters.
* @param secretKey The pre-shared secret key used by the sender of the request to create the signature.
* @param currentTimeMillis The current time, in milliseconds.
*
* @return true if the signature is correct for this request and secret key.
*/
protected static boolean verifyRequestSignature(HttpServletRequest request,
protected static boolean verifyRequestSignature(String contentType,
InputStream inputStream,
Map<String, String[]> parameterMap,
String secretKey,
long currentTimeMillis) {
return verifyRequestSignature(request, secretKey, currentTimeMillis, HashUtil.HashType.MD5);
return verifyRequestSignature(contentType, inputStream, parameterMap,
secretKey, currentTimeMillis, HashUtil.HashType.MD5
);
}

/**
* Verifies the signature in an HttpServletRequest.
*
* @param request The HttpServletRequest to be verified.
* @param contentType The request Content-Type header.
* @param inputStream The request data stream.
* @param parameterMap The request parameters.
* @param secretKey The pre-shared secret key used by the sender of the request to create the signature.
* @param currentTimeMillis The current time, in milliseconds.
* @param hashType Hash type to be used to construct request parameters.
*
* @return true if the signature is correct for this request and secret key.
*/
protected static boolean verifyRequestSignature(HttpServletRequest request,
protected static boolean verifyRequestSignature(String contentType,
InputStream inputStream,
Map<String, String[]> parameterMap,
String secretKey,
long currentTimeMillis,
HashUtil.HashType hashType) {

// Construct a sorted list of the name-value pair parameters supplied in the request, excluding the signature parameter
Map<String, String> sortedParams = new TreeMap<>();
if (request.getContentType() != null && request.getContentType().equals(APPLICATION_JSON)) {
if (APPLICATION_JSON.equals(contentType) && inputStream != null) {
ObjectMapper mapper = new ObjectMapper();
try{
Map<String,String> params = mapper.readValue(request.getInputStream(), new TypeReference<Map<String,String>>(){});
try {
Map<String,String> params = mapper.readValue(inputStream, new TypeReference<Map<String,String>>(){});
for (Map.Entry<String, String> entry : params.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
Expand All @@ -224,11 +250,11 @@ protected static boolean verifyRequestSignature(HttpServletRequest request,
}
}
else {
for (Map.Entry<String, String[]> entry: request.getParameterMap().entrySet()) {
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue()[0];
log.info(name + " = " + value);
if (value == null || value.trim().equals("")) {
if (value == null || value.trim().isEmpty()) {
continue;
}
sortedParams.put(name, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@
* Note: This servlet will immediately ack the callback as soon as it is validated. Your subclass will
* consume the callback object asynchronously. This is because it is important to keep latency of
* the acknowledgement to a minimum in order to maintain throughput when operating at any sort of volume.
* You are responsible for persisting this object in the event of any failure whilst processing
* You are responsible for persisting this object in the event of any failure whilst processing.
*
* @deprecated This class is no longer maintained and may be removed in a future release.
*/
@Deprecated
public abstract class AbstractMOServlet extends HttpServlet {

private static final long serialVersionUID = 8745764381059238419L;
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/com/vonage/client/voice/ncco/package-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,5 @@
/**
* Provides useful NCCO classes which can be serialized using Jackson when
* implementing webhooks to drive the Vonage Voice API.
* <p>
* The simplest way to use these classes is to subclass {@link com.vonage.client.voice.servlet.AbstractAnswerServlet}
* and implement {@code handleRequest(javax.servlet.http.HttpServletRequest)}.
* the returned NCCOResponse will automatically be serialized correctly.
*/
package com.vonage.client.voice.ncco;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
* Implement {@link #handleRequest(HttpServletRequest)} to return an {@link NccoResponse} and this servlet will
* ensure that the response is serialized correctly for the Vonage Voice API.
* </p>
*
* @deprecated This class is no longer maintained and may be removed in a future release.
*/
@Deprecated
public abstract class AbstractAnswerServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@

/**
* Provides an abstract Servlet for building NCCO webhooks for the Vonage Voice API.
*
* @deprecated This package is no longer maintained and may be removed or substantially refactored in a future release.
*/
package com.vonage.client.voice.servlet;
68 changes: 44 additions & 24 deletions src/test/java/com/vonage/client/auth/RequestSigningTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,67 +132,86 @@ private static Map<String, String> constructParamMap(List<NameValuePair> params)

@Test
public void testVerifyRequestSignature() {
HttpServletRequest request = constructDummyRequest();
assertTrue(RequestSigning.verifyRequestSignature(request, "abcde", 2100000));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, constructDummyParams(),
"abcde", 2100000
));
}

@Test
public void testVerifyRequestSignatureWithSha1Hash() {
Map<String, String[]> params = constructDummyParams();
params.put("sig", new String[]{"b7f749de27b4adcf736cc95c9a7e059a16c85127"});

assertTrue(RequestSigning.verifyRequestSignature(constructDummyRequest(params), "abcde", 2100000, HashUtil.HashType.HMAC_SHA1));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, params,
"abcde", 2100000, HashUtil.HashType.HMAC_SHA1
));
}

@Test
public void testVerifySignatureRequestJson(){
HttpServletRequest request = ConstructDummyRequestJson();
assertTrue(RequestSigning.verifyRequestSignature(request,"abcde",2100000, HashUtil.HashType.HMAC_SHA1));
public void testVerifySignatureRequestJson() throws Exception {
HttpServletRequest request = constructDummyRequestJson();
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, request.getInputStream(), constructDummyParams(),
"abcde", 2100000, HashUtil.HashType.HMAC_SHA1
));
}

@Test
public void testVerifyRequestSignatureWithHmacSha256Hash() {
Map<String, String[]> params = constructDummyParams();
params.put("sig", new String[]{"8d1b0428276b6a070578225914c3502cc0687a454dfbbbb370c76a14234cb546"});

assertTrue(RequestSigning.verifyRequestSignature(constructDummyRequest(params), "abcde", 2100000, HashUtil.HashType.HMAC_SHA256));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, params,
"abcde", 2100000, HashUtil.HashType.HMAC_SHA256
));
}

@Test
public void testVerifyRequestSignatureWithHmacMd5Hash() {
public void testVerifyRequestSignatureWithHmacMd5Hash() throws Exception {
Map<String, String[]> params = constructDummyParams();
params.put("sig", new String[]{"e0afe267aefd6dd18a848c1681517a19"});

assertTrue(RequestSigning.verifyRequestSignature(constructDummyRequest(params), "abcde", 2100000, HashUtil.HashType.HMAC_MD5));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, params,
"abcde", 2100000, HashUtil.HashType.HMAC_MD5
));
}

@Test
public void testVerifyRequestSignatureWithHmacSha512Hash() {
Map<String, String[]> params = constructDummyParams();
params.put("sig", new String[]{"1c834a1f6a377d4473971387b065cb38e2ad6c4869ba77b7b53e207a344e87ba04b456dfc697b371a2d1ce476d01dafd4394aa97525eff23badad39d2389a710"});

assertTrue(RequestSigning.verifyRequestSignature(constructDummyRequest(params), "abcde", 2100000, HashUtil.HashType.HMAC_SHA512));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, params,
"abcde", 2100000, HashUtil.HashType.HMAC_SHA512
));
}

@Test
public void testVerifyRequestSignatureNoSig() {
HttpServletRequest request = constructDummyRequest(constructDummyParamsNoSignature());

assertFalse(RequestSigning.verifyRequestSignature(request, "abcde", 2100000));
assertFalse(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, constructDummyParamsNoSignature(),
"abcde", 2100000
));
}

@Test
public void testVerifyRequestSignatureBadTimestamp() {
HttpServletRequest request = constructDummyRequest(constructDummyParamsInvalidTimestamp());

assertFalse(RequestSigning.verifyRequestSignature(request, "abcde", 2100000));
assertFalse(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, constructDummyParamsInvalidTimestamp(),
"abcde", 2100000
));
}

@Test
public void testVerifyRequestSignatureMissingTimestamp() {
HttpServletRequest request = constructDummyRequest(constructDummyParamsNoTimestamp());

assertFalse(RequestSigning.verifyRequestSignature(request, "abcde", 2100000));
assertFalse(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, constructDummyParamsNoTimestamp(),
"abcde", 2100000
));
}

@Test
Expand All @@ -201,17 +220,18 @@ public void testVerifyRequestSignatureHandlesNullParams() {
params.put("b", new String[]{ null });
params.put("sig", new String[]{"a3368bf718ba104dcb392d8877e8eb2b"});

HttpServletRequest request = constructDummyRequest(params);

assertTrue(RequestSigning.verifyRequestSignature(request, "abcde", 2100000));
assertTrue(RequestSigning.verifyRequestSignature(
RequestSigning.APPLICATION_JSON, null, params,
"abcde", 2100000
));
}

private HttpServletRequest constructDummyRequest() {
return constructDummyRequest(null);
}


private HttpServletRequest ConstructDummyRequestJson() {
private HttpServletRequest constructDummyRequestJson() {
MockHttpServletRequest request = new MockHttpServletRequest();
String dummyJson = "{\"a\":\"alphabet\",\"b\":\"bananas\",\"timestamp\":\"2100\",\"sig\":\"b7f749de27b4adcf736cc95c9a7e059a16c85127\"}";
request.setContent(dummyJson.getBytes());
Expand Down

0 comments on commit 4c31dc5

Please sign in to comment.