diff --git a/src/main/java/com/wultra/app/mobileutilityserver/config/CacheConfiguration.java b/src/main/java/com/wultra/app/mobileutilityserver/config/CacheConfiguration.java
new file mode 100644
index 00000000..28d90d60
--- /dev/null
+++ b/src/main/java/com/wultra/app/mobileutilityserver/config/CacheConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * Wultra Mobile Utility Server
+ * Copyright (C) 2023 Wultra s.r.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.wultra.app.mobileutilityserver.config;
+
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configuration for caching.
+ *
+ * @author Petr Dvorak, petr@wultra.com
+ */
+@Configuration
+@EnableCaching
+public class CacheConfiguration {
+
+ public static final String CERTIFICATE_FINGERPRINTS = "certificateFingerprints";
+ public static final String MOBILE_APPS = "mobileApps";
+ public static final String PRIVATE_KEYS = "privateKeys";
+
+}
diff --git a/src/main/java/com/wultra/app/mobileutilityserver/rest/filter/ResponseSignFilter.java b/src/main/java/com/wultra/app/mobileutilityserver/rest/filter/ResponseSignFilter.java
index 45e096ef..c136f5eb 100644
--- a/src/main/java/com/wultra/app/mobileutilityserver/rest/filter/ResponseSignFilter.java
+++ b/src/main/java/com/wultra/app/mobileutilityserver/rest/filter/ResponseSignFilter.java
@@ -19,8 +19,8 @@
import com.wultra.app.mobileutilityserver.rest.http.HttpHeaders;
import com.wultra.app.mobileutilityserver.rest.http.QueryParams;
-import com.wultra.app.mobileutilityserver.rest.service.MobileAppService;
import com.wultra.app.mobileutilityserver.rest.service.CryptographicOperationsService;
+import com.wultra.app.mobileutilityserver.rest.service.MobileAppService;
import io.getlime.security.powerauth.crypto.lib.model.exception.CryptoProviderException;
import io.getlime.security.powerauth.crypto.lib.model.exception.GenericCryptoException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -47,6 +47,8 @@
@Component
public class ResponseSignFilter extends OncePerRequestFilter {
+ private static final String PATH_PREFIX = "/app/init";
+
private final MobileAppService mobileAppService;
private final CryptographicOperationsService cryptographicOperationsService;
@@ -57,6 +59,12 @@ public ResponseSignFilter(MobileAppService mobileAppService, CryptographicOperat
this.cryptographicOperationsService = cryptographicOperationsService;
}
+ @Override
+ protected boolean shouldNotFilter(HttpServletRequest request) {
+ final String url = request.getRequestURI();
+ return !url.startsWith(PATH_PREFIX);
+ }
+
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain chain) throws ServletException, IOException {
final ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
diff --git a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/AdminService.java b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/AdminService.java
index 4b29083b..12941d3a 100644
--- a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/AdminService.java
+++ b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/AdminService.java
@@ -18,6 +18,7 @@
package com.wultra.app.mobileutilityserver.rest.service;
+import com.wultra.app.mobileutilityserver.config.CacheConfiguration;
import com.wultra.app.mobileutilityserver.database.model.CertificateEntity;
import com.wultra.app.mobileutilityserver.database.model.MobileAppEntity;
import com.wultra.app.mobileutilityserver.database.model.MobileDomainEntity;
@@ -41,6 +42,8 @@
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.openssl.PEMParser;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import javax.net.ssl.HttpsURLConnection;
@@ -73,6 +76,8 @@ public class AdminService {
private final CertificateConverter certificateConverter;
private final MobileAppConverter mobileAppConverter;
+ private final CacheManager cacheManager;
+
private final CryptographicOperationsService cryptographicOperationsService;
@Autowired
@@ -80,12 +85,13 @@ public AdminService(MobileAppRepository mobileAppRepository,
CertificateRepository certificateRepository,
MobileDomainRepository mobileDomainRepository,
CertificateConverter certificateConverter,
- MobileAppConverter mobileAppConverter, CryptographicOperationsService cryptographicOperationsService) {
+ MobileAppConverter mobileAppConverter, CacheManager cacheManager, CryptographicOperationsService cryptographicOperationsService) {
this.mobileAppRepository = mobileAppRepository;
this.certificateRepository = certificateRepository;
this.mobileDomainRepository = mobileDomainRepository;
this.certificateConverter = certificateConverter;
this.mobileAppConverter = mobileAppConverter;
+ this.cacheManager = cacheManager;
this.cryptographicOperationsService = cryptographicOperationsService;
}
@@ -121,6 +127,8 @@ public ApplicationDetailResponse createApplication(CreateApplicationRequest requ
final MobileAppEntity savedMobileAppEntity = mobileAppRepository.save(mobileAppEntity);
+ evictMobileAppAndPrivateKeyCache(name);
+
return mobileAppConverter.convertMobileApp(savedMobileAppEntity);
} catch (CryptoProviderException e) {
throw new AppException("Error while generating cryptographic keys", e);
@@ -185,6 +193,8 @@ public CertificateDetailResponse createApplicationCertificate(String appName, Cr
final CertificateEntity savedCertificateEntity = certificateRepository.save(certificateEntity);
+ evictCertificateFingerprintCache(appName);
+
final CertificateDetailResponse response = certificateConverter.convertCertificateDetailResponse(savedCertificateEntity);
logger.info("Certificate refreshed: {}", response);
return response;
@@ -244,6 +254,7 @@ public void deleteCertificate(String appName, String domain, String fingerprint)
if (certificate.getFingerprint().equalsIgnoreCase(fingerprint)) {
certificates.remove(certificate);
mobileDomainRepository.save(mobileDomainEntity);
+ evictCertificateFingerprintCache(appName);
return;
}
}
@@ -252,11 +263,49 @@ public void deleteCertificate(String appName, String domain, String fingerprint)
@Transactional
public void deleteDomain(String appName, String domain) {
mobileDomainRepository.deleteByAppNameAndDomain(appName, domain);
+ evictCertificateFingerprintCache(appName);
}
@Transactional
public void deleteExpiredCertificates() {
certificateRepository.deleteAllByExpiresBefore(new Date().getTime() / 1000);
+ invalidateCertificateCache();
+ }
+
+ /**
+ * Evict caches for mobile apps and private keys for given app name.
+ * @param appName App Name.
+ */
+ private void evictMobileAppAndPrivateKeyCache(String appName) {
+ final Cache mobileAppsCache = cacheManager.getCache(CacheConfiguration.MOBILE_APPS);
+ if (mobileAppsCache != null) {
+ mobileAppsCache.evict(appName);
+ }
+ final Cache privateKeyCache = cacheManager.getCache(CacheConfiguration.PRIVATE_KEYS);
+ if (privateKeyCache != null) {
+ privateKeyCache.evict(appName);
+ }
+ }
+
+ /**
+ * Evict certificate fingerprint cache for a given app name.
+ * @param appName App name.
+ */
+ private void evictCertificateFingerprintCache(String appName) {
+ final Cache cache = cacheManager.getCache(CacheConfiguration.CERTIFICATE_FINGERPRINTS);
+ if (cache != null) {
+ cache.evict(appName);
+ }
+ }
+
+ /**
+ * Invalidate values in the certificate cache.
+ */
+ private void invalidateCertificateCache() {
+ final Cache cache = cacheManager.getCache(CacheConfiguration.CERTIFICATE_FINGERPRINTS);
+ if (cache != null) {
+ cache.invalidate();
+ }
}
}
diff --git a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/CertificateFingerprintService.java b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/CertificateFingerprintService.java
index 70d0df41..2753804f 100644
--- a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/CertificateFingerprintService.java
+++ b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/CertificateFingerprintService.java
@@ -18,11 +18,13 @@
package com.wultra.app.mobileutilityserver.rest.service;
+import com.wultra.app.mobileutilityserver.config.CacheConfiguration;
import com.wultra.app.mobileutilityserver.database.model.CertificateEntity;
import com.wultra.app.mobileutilityserver.database.repo.CertificateRepository;
import com.wultra.app.mobileutilityserver.rest.model.converter.CertificateConverter;
import com.wultra.app.mobileutilityserver.rest.model.entity.CertificateFingerprint;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@@ -54,6 +56,7 @@ public CertificateFingerprintService(CertificateRepository repo, CertificateConv
* @return Collection with SSL pinning fingerprints, possibly empty.
*/
@Transactional
+ @Cacheable(cacheNames = CacheConfiguration.CERTIFICATE_FINGERPRINTS)
public List findCertificateFingerprintsByAppName(String appName) {
final List fingerprints = repo.findAllByAppName(appName);
final List result = new ArrayList<>();
diff --git a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/MobileAppService.java b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/MobileAppService.java
index 800fcfd7..0b6867f9 100644
--- a/src/main/java/com/wultra/app/mobileutilityserver/rest/service/MobileAppService.java
+++ b/src/main/java/com/wultra/app/mobileutilityserver/rest/service/MobileAppService.java
@@ -17,9 +17,11 @@
*/
package com.wultra.app.mobileutilityserver.rest.service;
+import com.wultra.app.mobileutilityserver.config.CacheConfiguration;
import com.wultra.app.mobileutilityserver.database.model.MobileAppEntity;
import com.wultra.app.mobileutilityserver.database.repo.MobileAppRepository;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
@@ -42,6 +44,7 @@ public MobileAppService(MobileAppRepository repo) {
* @param appName App name.
* @return True in case the app with given name exists, false otherwise.
*/
+ @Cacheable(cacheNames = CacheConfiguration.MOBILE_APPS)
public boolean appExists(String appName) {
return repo.existsByName(appName);
}
@@ -53,6 +56,7 @@ public boolean appExists(String appName) {
* @return Private key encoded as Base64 representation of the embedded big integer, or null
* if app with provided name does not exist.
*/
+ @Cacheable(cacheNames = CacheConfiguration.PRIVATE_KEYS)
public String privateKey(String appName) {
final MobileAppEntity mobileAppEntity = repo.findFirstByName(appName);
if (mobileAppEntity == null) {