From ec4f9094e6e6d22d278891832add7e869aed15e6 Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Thu, 15 Sep 2022 14:12:42 +0200 Subject: [PATCH 1/2] GH-125 simplification : got rid of all the unwanted dependencies, fixed a few issues, and trimmed down the code --- core/pom.xml | 19 --- .../adobe/aio/cache/CaffeinCacheUtils.java | 40 ----- .../com/adobe/aio/retrofit/RetrofitUtils.java | 55 ------- events_webhook/pom.xml | 40 ----- .../event/webhook/api/PublicKeyCdnApi.java | 1 + .../aio/event/webhook/cache/CacheService.java | 27 ---- .../event/webhook/cache/CacheServiceImpl.java | 89 ----------- .../event/webhook/model/CacheableObject.java | 66 -------- .../event/webhook/service/EventVerifier.java | 144 +++++------------- .../event/webhook/service/PubKeyService.java | 1 + .../webhook/service/EventVerifierTest.java | 14 +- pom.xml | 77 ---------- 12 files changed, 48 insertions(+), 525 deletions(-) delete mode 100644 core/src/main/java/com/adobe/aio/cache/CaffeinCacheUtils.java delete mode 100644 core/src/main/java/com/adobe/aio/retrofit/RetrofitUtils.java delete mode 100644 events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheService.java delete mode 100644 events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheServiceImpl.java delete mode 100644 events_webhook/src/main/java/com/adobe/aio/event/webhook/model/CacheableObject.java diff --git a/core/pom.xml b/core/pom.xml index 789db277..54f3f509 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -68,24 +68,5 @@ slf4j-simple - - com.github.ben-manes.caffeine - caffeine - - - - com.squareup.retrofit2 - retrofit - - - - com.squareup.retrofit2 - converter-jackson - - - - com.squareup.retrofit2 - converter-scalars - diff --git a/core/src/main/java/com/adobe/aio/cache/CaffeinCacheUtils.java b/core/src/main/java/com/adobe/aio/cache/CaffeinCacheUtils.java deleted file mode 100644 index 2aae5aa9..00000000 --- a/core/src/main/java/com/adobe/aio/cache/CaffeinCacheUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -package com.adobe.aio.cache; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CaffeinCacheUtils { - - private static final Logger logger = LoggerFactory.getLogger(CaffeinCacheUtils.class); - - private CaffeinCacheUtils() { - throw new IllegalStateException("This class is not meant to be instantiated."); - } - - public static Cache buildCacheWithExpiryAfterWrite(String cacheName, - long expiryInMinutes, long maxEntryCount) { - - logger.info("Initializing cache: {} with expiry-after-write: {} minutes, maxEntryCount: {}", - cacheName, expiryInMinutes, maxEntryCount); - - return Caffeine.newBuilder() - .expireAfterWrite(expiryInMinutes, TimeUnit.MINUTES) - .maximumSize(maxEntryCount) - .build(); - } -} - diff --git a/core/src/main/java/com/adobe/aio/retrofit/RetrofitUtils.java b/core/src/main/java/com/adobe/aio/retrofit/RetrofitUtils.java deleted file mode 100644 index 164b052b..00000000 --- a/core/src/main/java/com/adobe/aio/retrofit/RetrofitUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -package com.adobe.aio.retrofit; - -import com.adobe.aio.util.JacksonUtil; -import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import retrofit2.Converter.Factory; -import retrofit2.Retrofit; -import retrofit2.Retrofit.Builder; -import retrofit2.converter.jackson.JacksonConverterFactory; -import retrofit2.converter.scalars.ScalarsConverterFactory; - - -public class RetrofitUtils { - - /** - * Scalars converter supports converting strings and both primitives and their boxed types to - * text/plain bodies. - */ - private static Builder getRetrofitBuilderWithScalarsConverter(String url, - int readTimeoutInSeconds) { - Builder builder = new Builder(); - OkHttpClient okHttpClient = new OkHttpClient().newBuilder(). - readTimeout(readTimeoutInSeconds, TimeUnit.SECONDS).build(); - builder.baseUrl(url); - builder.addConverterFactory(ScalarsConverterFactory.create()); - builder.client(okHttpClient); - return builder; - } - - private static Builder getRetrofitBuilder(String url, int readTimeoutInSeconds, - Factory converterFactory) { - return getRetrofitBuilderWithScalarsConverter(url, readTimeoutInSeconds) - .addConverterFactory(converterFactory); - } - - /** - * @return Retrofit with a jackson converter - */ - public static Retrofit getRetrofitWithJacksonConverterFactory(String url, - int readTimeoutInSeconds) { - return getRetrofitBuilder(url, readTimeoutInSeconds, - JacksonConverterFactory.create(JacksonUtil.DEFAULT_OBJECT_MAPPER)).build(); - } -} diff --git a/events_webhook/pom.xml b/events_webhook/pom.xml index 798d0567..fc1d5ac2 100644 --- a/events_webhook/pom.xml +++ b/events_webhook/pom.xml @@ -45,52 +45,12 @@ slf4j-api - - com.squareup.retrofit2 - retrofit - - - - com.h2database - h2 - - - - commons-validator - commons-validator - - - - com.github.ben-manes.caffeine - caffeine - - - - commons-codec - commons-codec - - - - com.google.guava - guava - - - - org.springframework - spring-context - - org.mockito mockito-core - - org.springframework.boot - spring-boot-test - - junit junit diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/api/PublicKeyCdnApi.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/api/PublicKeyCdnApi.java index 6e9bcba7..8c470ebb 100644 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/api/PublicKeyCdnApi.java +++ b/events_webhook/src/main/java/com/adobe/aio/event/webhook/api/PublicKeyCdnApi.java @@ -15,6 +15,7 @@ import feign.RequestLine; public interface PublicKeyCdnApi { + @RequestLine(value = "GET {pubKeyPath}") String getPubKeyFromCDN(@Param("pubKeyPath") String pubKeyPath); } diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheService.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheService.java deleted file mode 100644 index 4750a12c..00000000 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheService.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -package com.adobe.aio.event.webhook.cache; - -import com.adobe.aio.event.webhook.model.CacheableObject; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public interface CacheService { - - @Nullable - Object get(@Nonnull String key); - - void put(@Nonnull String key, @Nonnull Object value); - - boolean isExpired(CacheableObject cacheable); - -} diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheServiceImpl.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheServiceImpl.java deleted file mode 100644 index 31ea52be..00000000 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/cache/CacheServiceImpl.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -package com.adobe.aio.event.webhook.cache; - -import com.adobe.aio.event.webhook.model.CacheableObject; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class CacheServiceImpl implements CacheService { - - private static final Logger logger = LoggerFactory.getLogger(CacheServiceImpl.class); - - Map cacheMap; - Date cacheExpirationDate; - - @Nullable - @Override - public String get(@Nonnull String key) { - CacheableObject obj = (CacheableObject) cacheMap.get(key); - if (obj != null) { - if (isExpired(obj)) { - logger.debug("cache is expired..invalidating entry for key {}", key); - cacheMap.remove(key); - return null; - } - else { - return obj.getValue(); - } - } - return null; - } - - @Override - public void put(@Nonnull String key, @Nonnull Object value) { - CacheableObject cacheableObject = new CacheableObject(key, (String) value, 1440); - cacheMap.put(key, cacheableObject); - } - - @Override - public boolean isExpired(CacheableObject cacheableObject) { - return getExpirationDate(cacheableObject.getExpiryInMinutes()).after(this.cacheExpirationDate); - } - - private CacheServiceImpl buildWithExpiry(int ttl) { - this.cacheExpirationDate = getExpirationDate(ttl); - return this; - } - - private CacheServiceImpl initialiseCacheMap() { - this.cacheMap = new HashMap<>(); - return this; - } - - public static CacheBuilder cacheBuilder() { - return new CacheBuilder(); - } - - public static class CacheBuilder { - public CacheServiceImpl buildWithExpiry(int expiryInMinutes) { - return new CacheServiceImpl() - .initialiseCacheMap() - .buildWithExpiry(expiryInMinutes); - } - } - - private Date getExpirationDate(int minutesToLive) { - Date expirationDate = new java.util.Date(); - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime(expirationDate); - cal.add(cal.MINUTE, minutesToLive); - expirationDate = cal.getTime(); - return expirationDate; - } -} diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/model/CacheableObject.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/model/CacheableObject.java deleted file mode 100644 index 26f13f5d..00000000 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/model/CacheableObject.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -package com.adobe.aio.event.webhook.model; - -import java.util.Objects; - -public class CacheableObject { - - private String key; - private String value; - private int expiryInMinutes; - - public CacheableObject(String key, String value, int expiryInMinutes) { - this.key = key; - this.value = value; - this.expiryInMinutes = expiryInMinutes; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - public int getExpiryInMinutes() { - return expiryInMinutes; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CacheableObject that = (CacheableObject) o; - return expiryInMinutes == that.expiryInMinutes && Objects.equals(key, that.key) - && Objects.equals(value, that.value); - } - - @Override - public int hashCode() { - return Objects.hash(key, value, expiryInMinutes); - } - - @Override - public String toString() { - return "CacheableObject{" + - "key='" + key + '\'' + - ", value='" + value + '\'' + - ", expiryInMinutes=" + expiryInMinutes + - '}'; - } -} diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java index 872031eb..7abc1670 100644 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java +++ b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java @@ -11,63 +11,55 @@ */ package com.adobe.aio.event.webhook.service; -import static com.adobe.aio.event.webhook.cache.CacheServiceImpl.cacheBuilder; import static java.nio.charset.StandardCharsets.UTF_8; -import com.adobe.aio.event.webhook.cache.CacheServiceImpl; import com.adobe.aio.event.webhook.feign.FeignPubKeyService; import com.adobe.aio.exception.AIOException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.security.InvalidKeyException; +import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; -import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Map; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.validator.routines.UrlValidator; -import org.h2.util.StringUtils; +import java.util.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -@Service public class EventVerifier { - private static Logger logger = LoggerFactory.getLogger(EventVerifier.class); - - public static final String ADOBE_IOEVENTS_SECURITY_DOMAIN = "https://static.adobeioevents.com/"; + public static final String ADOBE_IOEVENTS_SECURITY_DOMAIN = "https://static.adobeioevents.com"; public static final String ADOBE_IOEVENTS_DIGI_SIGN_1 = "x-adobe-digital-signature-1"; public static final String ADOBE_IOEVENTS_DIGI_SIGN_2 = "x-adobe-digital-signature-2"; public static final String ADOBE_IOEVENTS_PUB_KEY_1_PATH = "x-adobe-public-key1-path"; public static final String ADOBE_IOEVENTS_PUB_KEY_2_PATH = "x-adobe-public-key2-path"; - private static final int CACHE_EXPIRY_IN_MINUTES = 1440; // expiry of 24 hrs + private static final Logger logger = LoggerFactory.getLogger(EventVerifier.class); + private final FeignPubKeyService pubKeyService; - private final CacheServiceImpl pubKeyCache; - private FeignPubKeyService pubKeyService; + EventVerifier(String url) { + this.pubKeyService = new FeignPubKeyService(url); + } public EventVerifier() { - this.pubKeyCache = cacheBuilder().buildWithExpiry(CACHE_EXPIRY_IN_MINUTES); - this.pubKeyService = new FeignPubKeyService(ADOBE_IOEVENTS_SECURITY_DOMAIN); + this(ADOBE_IOEVENTS_SECURITY_DOMAIN); } /** - * Authenticate the event by checking the target recipient - * and verifying the signatures - * @param message - the event payload + * Authenticate the event by checking the target recipient and verifying the signatures + * + * @param message - the event payload * @param clientId - recipient client id in the payload - * @param headers - webhook request headers + * @param headers - webhook request headers * @return boolean - TRUE if valid event else FALSE * @throws Exception */ public boolean authenticateEvent(String message, String clientId, Map headers) { - if(!isValidTargetRecipient(message, clientId)) { + if (!isValidTargetRecipient(message, clientId)) { logger.error("target recipient {} is not valid for message {}", clientId, message); return false; } @@ -78,120 +70,56 @@ public boolean authenticateEvent(String message, String clientId, return true; } - private boolean verifyEventSignatures(String message, - Map headers) { + private boolean verifyEventSignatures(String message, Map headers) { String[] digitalSignatures = {headers.get(ADOBE_IOEVENTS_DIGI_SIGN_1), headers.get(ADOBE_IOEVENTS_DIGI_SIGN_2)}; String[] pubKeyPaths = {headers.get(ADOBE_IOEVENTS_PUB_KEY_1_PATH), headers.get(ADOBE_IOEVENTS_PUB_KEY_2_PATH)}; - String publicKey1Url = ADOBE_IOEVENTS_SECURITY_DOMAIN + headers.get(ADOBE_IOEVENTS_PUB_KEY_1_PATH); - String publicKey2Url = ADOBE_IOEVENTS_SECURITY_DOMAIN + headers.get(ADOBE_IOEVENTS_PUB_KEY_2_PATH); - - try { - if (isValidUrl(publicKey1Url) && isValidUrl(publicKey2Url)) { - return verifySignature(message, pubKeyPaths, digitalSignatures); - } - } catch (Exception e) { - throw new AIOException("Error verifying signature for public keys " + publicKey1Url + - " & " + publicKey2Url + ". Reason -> " + e.getMessage()); - } - return false; + return verifySignature(message, pubKeyPaths, digitalSignatures); } - private boolean verifySignature(String message, String[] publicKeyPaths, String[] signatures) - throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { + private boolean verifySignature(String message, String[] publicKeyPaths, String[] signatures) { byte[] data = message.getBytes(UTF_8); - + boolean result = true; for (int i = 0; i < signatures.length; i++) { - // signature generated at I/O Events side is Base64 encoded, so it must be decoded - byte[] sign = Base64.decodeBase64(signatures[i]); - Signature sig = Signature.getInstance("SHA256withRSA"); - sig.initVerify(getPublic(fetchPemEncodedPublicKey(publicKeyPaths[i]))); - sig.update(data); - boolean result = sig.verify(sign); - if (result) { - return true; + try { + // signature generated at I/O Events side is Base64 encoded, so it must be decoded + byte[] sign = Base64.getDecoder().decode(signatures[i]); + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(getPublicKey(publicKeyPaths[i])); + sig.update(data); + result = result && sig.verify(sign); + } catch (GeneralSecurityException e) { + throw new AIOException("Error verifying signature for public key " + publicKeyPaths[i] + +". Reason -> " + e.getMessage(), e); } } - return false; + return result; } private boolean isValidTargetRecipient(String message, String clientId) { - ObjectMapper mapper = new ObjectMapper(); try { + ObjectMapper mapper = new ObjectMapper(); JsonNode jsonPayload = mapper.readTree(message); JsonNode recipientClientIdNode = jsonPayload.get("recipient_client_id"); - if (recipientClientIdNode != null) { - return recipientClientIdNode.textValue().equals(clientId); - } + return (recipientClientIdNode != null && recipientClientIdNode.textValue() !=null + && recipientClientIdNode.textValue().equals(clientId)); } catch (JsonProcessingException e) { throw new AIOException("error parsing the event payload during target recipient check.."); } - return false; } - private PublicKey getPublic(String pubKey) + private PublicKey getPublicKey(String pubKeyPath) throws NoSuchAlgorithmException, InvalidKeySpecException { - String publicKeyPEM = pubKey + String publicKeyPEM = pubKeyService.getPubKeyFromCDN(pubKeyPath) .replace("-----BEGIN PUBLIC KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PUBLIC KEY-----", ""); - byte[] encoded = Base64.decodeBase64(publicKeyPEM); - + byte[] keyBytes = Base64.getDecoder().decode(publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); return keyFactory.generatePublic(keySpec); } - String fetchPemEncodedPublicKey(String publicKeyPath) { - return fetchKeyFromCacheOrApi(publicKeyPath); - } - - private String fetchKeyFromCacheOrApi(String pubKeyPath) { - String pubKeyFileName = getPublicKeyFileName(pubKeyPath); - String pubKey = getKeyFromCache(pubKeyFileName); - if (StringUtils.isNullOrEmpty(pubKey)) { - pubKey = fetchKeyFromApiAndPutInCache(pubKeyPath, pubKeyFileName); - } - return pubKey; - } - - private String fetchKeyFromApiAndPutInCache(String pubKeyPath, String pubKeyFileName) { - try { - logger.warn("public key {} not present in cache, fetching directly from the cdn url {}", - pubKeyFileName, ADOBE_IOEVENTS_SECURITY_DOMAIN + pubKeyPath); - String pubKeyFetchResponse = pubKeyService.getPubKeyFromCDN(pubKeyPath); - if (!StringUtils.isNullOrEmpty(pubKeyFetchResponse)) { - pubKeyCache.put(pubKeyFileName, pubKeyFetchResponse); - } - return pubKeyFetchResponse; - } catch (Exception e) { - throw new AIOException("error fetching public key from CDN url -> " - + ADOBE_IOEVENTS_SECURITY_DOMAIN + pubKeyPath + " due to " + e.getMessage()); - } - } - - private String getKeyFromCache(String pubKeyFileNameAsKey) { - Object pubKey = pubKeyCache.get(pubKeyFileNameAsKey); - if (pubKey != null) { - logger.debug("fetched key successfully for pub key path {} from cache", pubKeyFileNameAsKey); - return String.valueOf(pubKey); - } - return null; - } - - /** - * Parses the pub key file name from the relative path - * - * @param pubKeyPath - relative path in the format /prod/keys/pub-key-voy5XEbWmT.pem - * @return public key file name - */ - private String getPublicKeyFileName(String pubKeyPath) { - return pubKeyPath.substring(pubKeyPath.lastIndexOf('/') + 1); - } - - private boolean isValidUrl(String url) { - return new UrlValidator().isValid(url); - } } diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/PubKeyService.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/PubKeyService.java index 74026ef0..5262f10f 100644 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/PubKeyService.java +++ b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/PubKeyService.java @@ -12,5 +12,6 @@ package com.adobe.aio.event.webhook.service; public interface PubKeyService { + String getPubKeyFromCDN(String pubKeyPath); } diff --git a/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java b/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java index ceb88fb7..8b6fde7b 100644 --- a/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java +++ b/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java @@ -21,10 +21,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; -@RunWith(MockitoJUnitRunner.class) + public class EventVerifierTest { private static final String TEST_CLIENT_ID = "client_id1"; @@ -33,9 +31,17 @@ public class EventVerifierTest { private static final String TEST_DIGI_SIGN_2 = "GpMONiPMHY51vpHF3R9SSs9ogRn8i2or/bvV3R+PYXGgDzAxDhRdl9dIUp/qQ3vsxDGEv045IV4GQ2f4QbsFvWLJsBNyCqLs6KL8LsRoGfEC4Top6c1VVjrEEQ1MOoFcoq/6riXzg4h09lRTfARllVv+icgzAiuv/JW2HNg5yQ4bqenFELD6ipCStuaI/OGS0A9s0Hc6o3aoHz3r5d5DecwE6pUdpG8ODhKBM+34CvcvMDNdrj8STYWHsEUqGdR9klpaqaC1QRYFIO7WgbgdwsuGULz6Sjm+q5s5Wh++fz5E+gXkizFviD389gDIUylFTig/1h7WTLRDuSz69Q+C5w=="; private static final String TEST_PUB_KEY1_PATH = "qe/keys/pub-key-voy5XEbWmT.pem"; private static final String TEST_PUB_KEY2_PATH = "qe/keys/pub-key-maAv3Tg6ZH.pem"; - + static int port = 9999; private EventVerifier underTest; + // TODO use wiremock to stub https://static.adobeioevents.com + // and pass the wiremock url to the EventVerifier + // and use some test keys and pem + // because here you are doing an integration test not a simple junit test + // you actually need these public pem files to be available + //@Rule + //public WireMockRule wireMockRule = new WireMockRule(port); + @Before public void setup() { underTest = new EventVerifier(); diff --git a/pom.xml b/pom.xml index 6d597cdf..d5a556af 100644 --- a/pom.xml +++ b/pom.xml @@ -104,17 +104,6 @@ [1.7.21,1.7.25] 2.12.3 0.11.2 - 2.7.2 - 2.5.0 - 2.7.2 - 2.7.2 - 2.6.2 - 29.0-jre - 1.4.200 - 1.6 - 1.15 - 2.5.6 - 5.3.20 11.2 3.8.0 @@ -133,31 +122,6 @@ provided - - com.squareup.retrofit2 - converter-scalars - ${converter-scalars.version} - - - - com.squareup.retrofit2 - converter-jackson - ${converter-jackson.version} - - - - com.squareup.retrofit2 - retrofit - ${retrofit.version} - - - - com.github.ben-manes.caffeine - caffeine - ${caffeine.version} - compile - - org.apache.commons commons-text @@ -171,32 +135,6 @@ provided - - commons-codec - commons-codec - ${commons-codec.version} - - - - com.google.guava - guava - ${guava.version} - compile - - - - commons-validator - commons-validator - ${commons-validator.version} - - - - org.springframework - spring-context - ${spring-context.version} - compile - - org.slf4j slf4j-api @@ -204,12 +142,6 @@ provided - - com.h2database - h2 - ${h2.version} - - io.jsonwebtoken jjwt-api @@ -273,13 +205,6 @@ test - - org.springframework.boot - spring-boot-test - ${spring-boot-test.version} - test - - com.github.tomakehurst wiremock-jre8 @@ -308,8 +233,6 @@ test - - From 3098fa685ae83e9d36fcead6ba7ad54ef6e9cd50 Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Fri, 16 Sep 2022 16:19:13 +0200 Subject: [PATCH 2/2] GH-125 adding question/todo --- .../com/adobe/aio/event/webhook/service/EventVerifier.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java index 7abc1670..7be8a8f9 100644 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java +++ b/events_webhook/src/main/java/com/adobe/aio/event/webhook/service/EventVerifier.java @@ -89,6 +89,10 @@ private boolean verifySignature(String message, String[] publicKeyPaths, String[ sig.initVerify(getPublicKey(publicKeyPaths[i])); sig.update(data); result = result && sig.verify(sign); + // TODO may be this is not the behavior you had in mind ? + // both should succeed, but only one veification is enough + // I'm guessing we have 2 in case we cached an out-dated pem file + // we should document that further add a README file } catch (GeneralSecurityException e) { throw new AIOException("Error verifying signature for public key " + publicKeyPaths[i] +". Reason -> " + e.getMessage(), e);