Skip to content

Commit

Permalink
Fix #46: Improve performance by using cache on frequent queries
Browse files Browse the repository at this point in the history
  • Loading branch information
petrdvorak committed Mar 17, 2023
1 parent d522cd4 commit 0619112
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/

package com.wultra.app.mobileutilityserver.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

/**
* Configuration for caching.
*
* @author Petr Dvorak, [email protected]
*/
@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";

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -73,19 +76,22 @@ public class AdminService {
private final CertificateConverter certificateConverter;
private final MobileAppConverter mobileAppConverter;

private final CacheManager cacheManager;

private final CryptographicOperationsService cryptographicOperationsService;

@Autowired
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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<CertificateFingerprint> findCertificateFingerprintsByAppName(String appName) {
final List<CertificateEntity> fingerprints = repo.findAllByAppName(appName);
final List<CertificateFingerprint> result = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
}
Expand All @@ -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) {
Expand Down

0 comments on commit 0619112

Please sign in to comment.