Pull request #219: RED-4645: Multitenancy for storage
Merge in RED/redaction-report-service from RED-4645 to master * commit 'a6ea2335d7132ce0a91fd19b29e4c31bea28ba8c': RED-4645: Multitenancy for storage
This commit is contained in:
commit
e465c76d07
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<persistence-service.version>2.0.8</persistence-service.version>
|
<persistence-service.version>2.1.0</persistence-service.version>
|
||||||
<dsljson.version>1.9.9</dsljson.version>
|
<dsljson.version>1.9.9</dsljson.version>
|
||||||
<apache-poi.version>5.2.3</apache-poi.version>
|
<apache-poi.version>5.2.3</apache-poi.version>
|
||||||
</properties>
|
</properties>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.iqser.red</groupId>
|
<groupId>com.iqser.red</groupId>
|
||||||
<artifactId>platform-commons-dependency</artifactId>
|
<artifactId>platform-commons-dependency</artifactId>
|
||||||
<version>1.20.0</version>
|
<version>1.22.0</version>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|||||||
@ -13,6 +13,9 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
|||||||
import com.iqser.red.commons.spring.DefaultWebMvcConfiguration;
|
import com.iqser.red.commons.spring.DefaultWebMvcConfiguration;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.client.DossierClient;
|
import com.iqser.red.service.redaction.report.v1.server.client.DossierClient;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.configuration.MessagingConfiguration;
|
import com.iqser.red.service.redaction.report.v1.server.configuration.MessagingConfiguration;
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.multitenancy.AsyncConfig;
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.multitenancy.MultiTenancyMessagingConfiguration;
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.multitenancy.MultiTenancyWebConfiguration;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.settings.ReportTemplateSettings;
|
import com.iqser.red.service.redaction.report.v1.server.settings.ReportTemplateSettings;
|
||||||
|
|
||||||
import io.micrometer.core.aop.TimedAspect;
|
import io.micrometer.core.aop.TimedAspect;
|
||||||
@ -20,7 +23,7 @@ import io.micrometer.core.instrument.MeterRegistry;
|
|||||||
|
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class})
|
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class})
|
||||||
@Import({DefaultWebMvcConfiguration.class, MessagingConfiguration.class})
|
@Import({MultiTenancyWebConfiguration.class, AsyncConfig.class, MessagingConfiguration.class, MultiTenancyMessagingConfiguration.class})
|
||||||
@EnableFeignClients(basePackageClasses = {DossierClient.class})
|
@EnableFeignClients(basePackageClasses = {DossierClient.class})
|
||||||
@EnableConfigurationProperties(ReportTemplateSettings.class)
|
@EnableConfigurationProperties(ReportTemplateSettings.class)
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.client;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.internal.resources.TenantsResource;
|
||||||
|
|
||||||
|
@FeignClient(name = "TenantsResource", url = "${persistence-service.url}")
|
||||||
|
public interface TenantsClient extends TenantsResource {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class AsyncConfig extends AsyncConfigurerSupport {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Executor getAsyncExecutor() {
|
||||||
|
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
|
||||||
|
executor.setCorePoolSize(7);
|
||||||
|
executor.setMaxPoolSize(42);
|
||||||
|
executor.setQueueCapacity(11);
|
||||||
|
executor.setThreadNamePrefix("TenantAwareTaskExecutor-");
|
||||||
|
executor.setTaskDecorator(new TenantAwareTaskDecorator());
|
||||||
|
executor.initialize();
|
||||||
|
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class EncryptionDecryptionService {
|
||||||
|
|
||||||
|
@Value("${redaction-report-service.crypto.key:redaction}")
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
private SecretKey secretKey;
|
||||||
|
private byte[] iv;
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@PostConstruct
|
||||||
|
protected void postConstruct() {
|
||||||
|
|
||||||
|
SecureRandom secureRandom = new SecureRandom();
|
||||||
|
iv = new byte[12];
|
||||||
|
secureRandom.nextBytes(iv);
|
||||||
|
secretKey = generateSecretKey(key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public String encrypt(String strToEncrypt) {
|
||||||
|
|
||||||
|
return Base64.getEncoder().encodeToString(encrypt(strToEncrypt.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public String decrypt(String strToDecrypt) {
|
||||||
|
|
||||||
|
byte[] bytes = Base64.getDecoder().decode(strToDecrypt);
|
||||||
|
return new String(decrypt(bytes), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public byte[] encrypt(byte[] data) {
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||||
|
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
|
||||||
|
byte[] encryptedData = cipher.doFinal(data);
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + encryptedData.length);
|
||||||
|
byteBuffer.putInt(iv.length);
|
||||||
|
byteBuffer.put(iv);
|
||||||
|
byteBuffer.put(encryptedData);
|
||||||
|
return byteBuffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public byte[] decrypt(byte[] encryptedData) {
|
||||||
|
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData);
|
||||||
|
int noonceSize = byteBuffer.getInt();
|
||||||
|
if (noonceSize < 12 || noonceSize >= 16) {
|
||||||
|
throw new IllegalArgumentException("Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file.");
|
||||||
|
}
|
||||||
|
byte[] iv = new byte[noonceSize];
|
||||||
|
byteBuffer.get(iv);
|
||||||
|
|
||||||
|
SecretKey secretKey = generateSecretKey(key, iv);
|
||||||
|
|
||||||
|
byte[] cipherBytes = new byte[byteBuffer.remaining()];
|
||||||
|
byteBuffer.get(cipherBytes);
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||||
|
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
|
||||||
|
return cipher.doFinal(cipherBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public SecretKey generateSecretKey(String password, byte[] iv) {
|
||||||
|
|
||||||
|
KeySpec spec = new PBEKeySpec(password.toCharArray(), iv, 65536, 128); // AES-128
|
||||||
|
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||||
|
byte[] key = secretKeyFactory.generateSecret(spec).getEncoded();
|
||||||
|
return new SecretKeySpec(key, "AES");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ForwardTenantInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
|
public static final String TENANT_HEADER_NAME = "X-TENANT-ID";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(RequestTemplate template) {
|
||||||
|
template.header(TENANT_HEADER_NAME, TenantContext.getTenantId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import static com.iqser.red.service.redaction.report.v1.server.multitenancy.ForwardTenantInterceptor.TENANT_HEADER_NAME;
|
||||||
|
|
||||||
|
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MultiTenancyMessagingConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static BeanPostProcessor multitenancyBeanPostProcessor() {
|
||||||
|
|
||||||
|
return new BeanPostProcessor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
|
||||||
|
if (bean instanceof RabbitTemplate) {
|
||||||
|
|
||||||
|
((RabbitTemplate) bean).setBeforePublishPostProcessors(m -> {
|
||||||
|
m.getMessageProperties().setHeader(TENANT_HEADER_NAME, TenantContext.getTenantId());
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (bean instanceof AbstractRabbitListenerContainerFactory) {
|
||||||
|
|
||||||
|
((AbstractRabbitListenerContainerFactory<?>) bean).setAfterReceivePostProcessors(m -> {
|
||||||
|
String tenant = m.getMessageProperties().getHeader(TENANT_HEADER_NAME);
|
||||||
|
|
||||||
|
if (tenant != null) {
|
||||||
|
TenantContext.setTenantId(tenant);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("No Tenant is set queue message");
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
|
||||||
|
import com.iqser.red.commons.spring.DefaultWebMvcConfiguration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MultiTenancyWebConfiguration extends DefaultWebMvcConfiguration {
|
||||||
|
|
||||||
|
private final TenantInterceptor tenantInterceptor;
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MultiTenancyWebConfiguration(TenantInterceptor tenantInterceptor) {
|
||||||
|
|
||||||
|
this.tenantInterceptor = tenantInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
|
||||||
|
registry.addWebRequestInterceptor(tenantInterceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.client.TenantsClient;
|
||||||
|
import com.iqser.red.storage.commons.model.AzureStorageConnection;
|
||||||
|
import com.iqser.red.storage.commons.model.S3StorageConnection;
|
||||||
|
import com.iqser.red.storage.commons.service.StorageConnectionProvider;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StorageConnectionProviderImpl implements StorageConnectionProvider {
|
||||||
|
|
||||||
|
private final TenantsClient tenantsClient;
|
||||||
|
private final EncryptionDecryptionService encryptionDecryptionService;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AzureStorageConnection getAzureStorageConnection(String tenantId) {
|
||||||
|
|
||||||
|
var tenant = tenantsClient.getTenant(tenantId);
|
||||||
|
return AzureStorageConnection.builder()
|
||||||
|
.connectionString(encryptionDecryptionService.decrypt(tenant.getAzureStorageConnection().getConnectionString()))
|
||||||
|
.containerName(tenant.getAzureStorageConnection().getContainerName())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3StorageConnection getS3StorageConnection(String tenantId) {
|
||||||
|
|
||||||
|
var tenant = tenantsClient.getTenant(tenantId);
|
||||||
|
return S3StorageConnection.builder()
|
||||||
|
.key(tenant.getS3StorageConnection().getKey())
|
||||||
|
.secret(encryptionDecryptionService.decrypt(tenant.getS3StorageConnection().getSecret()))
|
||||||
|
.signerType(tenant.getS3StorageConnection().getSignerType())
|
||||||
|
.bucketName(tenant.getS3StorageConnection().getBucketName())
|
||||||
|
.region(tenant.getS3StorageConnection().getRegion())
|
||||||
|
.endpoint(tenant.getS3StorageConnection().getEndpoint())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import org.springframework.core.task.TaskDecorator;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
|
public class TenantAwareTaskDecorator implements TaskDecorator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Runnable decorate(@NonNull Runnable runnable) {
|
||||||
|
|
||||||
|
String tenantId = TenantContext.getTenantId();
|
||||||
|
return () -> {
|
||||||
|
try {
|
||||||
|
TenantContext.setTenantId(tenantId);
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
TenantContext.setTenantId(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public final class TenantContext {
|
||||||
|
|
||||||
|
private static InheritableThreadLocal<String> currentTenant = new InheritableThreadLocal<>();
|
||||||
|
|
||||||
|
|
||||||
|
public static void setTenantId(String tenantId) {
|
||||||
|
|
||||||
|
log.debug("Setting tenantId to " + tenantId);
|
||||||
|
currentTenant.set(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getTenantId() {
|
||||||
|
|
||||||
|
return currentTenant.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
|
||||||
|
currentTenant.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.iqser.red.service.redaction.report.v1.server.multitenancy;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TenantInterceptor implements WebRequestInterceptor {
|
||||||
|
|
||||||
|
public static final String TENANT_HEADER_NAME = "X-TENANT-ID";
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preHandle(WebRequest request) {
|
||||||
|
|
||||||
|
if (request.getHeader(TENANT_HEADER_NAME) != null) {
|
||||||
|
TenantContext.setTenantId(request.getHeader(TENANT_HEADER_NAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postHandle(WebRequest request, ModelMap model) {
|
||||||
|
|
||||||
|
TenantContext.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(WebRequest request, Exception ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
|
||||||
import com.iqser.red.service.redaction.report.v1.api.model.StoredFileInformation;
|
import com.iqser.red.service.redaction.report.v1.api.model.StoredFileInformation;
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.multitenancy.TenantContext;
|
||||||
import com.iqser.red.storage.commons.service.StorageService;
|
import com.iqser.red.storage.commons.service.StorageService;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -25,7 +26,7 @@ public class ReportStorageService {
|
|||||||
public String storeObject(String downloadId, byte[] data) {
|
public String storeObject(String downloadId, byte[] data) {
|
||||||
|
|
||||||
String storageId = StorageIdUtils.getStorageId(downloadId, UUID.randomUUID().toString());
|
String storageId = StorageIdUtils.getStorageId(downloadId, UUID.randomUUID().toString());
|
||||||
storageService.storeObject(storageId, new ByteArrayInputStream(data));
|
storageService.storeObject(TenantContext.getTenantId(), storageId, new ByteArrayInputStream(data));
|
||||||
return storageId;
|
return storageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ public class ReportStorageService {
|
|||||||
public String storeReportInformation(String downloadId, List<StoredFileInformation> storedFileInformations) {
|
public String storeReportInformation(String downloadId, List<StoredFileInformation> storedFileInformations) {
|
||||||
|
|
||||||
String storageId = StorageIdUtils.getStorageId(downloadId, "REPORT_INFO.json");
|
String storageId = StorageIdUtils.getStorageId(downloadId, "REPORT_INFO.json");
|
||||||
storageService.storeJSONObject(storageId, storedFileInformations);
|
storageService.storeJSONObject(TenantContext.getTenantId(), storageId, storedFileInformations);
|
||||||
return storageId;
|
return storageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,22 +43,23 @@ public class ReportStorageService {
|
|||||||
public byte[] getReportTemplate(String storageId) {
|
public byte[] getReportTemplate(String storageId) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return IOUtils.toByteArray(storageService.getObject(storageId).getInputStream());
|
return IOUtils.toByteArray(storageService.getObject(TenantContext.getTenantId(), storageId).getInputStream());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Could not get report template.");
|
throw new RuntimeException("Could not get report template.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public byte[] getStoredObjectBytes(String dossierId, String fileId, FileType fileType) {
|
public byte[] getStoredObjectBytes(String dossierId, String fileId, FileType fileType) {
|
||||||
|
|
||||||
return IOUtils.toByteArray(storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, fileType)).getInputStream());
|
return IOUtils.toByteArray(storageService.getObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType)).getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean objectExists(String dossierId, String fileId, FileType fileType) {
|
public boolean objectExists(String dossierId, String fileId, FileType fileType) {
|
||||||
|
|
||||||
return storageService.objectExists(StorageIdUtils.getStorageId(dossierId, fileId, fileType));
|
return storageService.objectExists(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,9 +26,6 @@ spring:
|
|||||||
max-interval: 15000
|
max-interval: 15000
|
||||||
prefetch: 1
|
prefetch: 1
|
||||||
|
|
||||||
platform.multi-tenancy:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoint:
|
endpoint:
|
||||||
metrics.enabled: ${monitoring.enabled:false}
|
metrics.enabled: ${monitoring.enabled:false}
|
||||||
@ -38,8 +35,4 @@ management:
|
|||||||
metrics.export.prometheus.enabled: ${monitoring.enabled:false}
|
metrics.export.prometheus.enabled: ${monitoring.enabled:false}
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
signer-type: 'AWSS3V4SignerType'
|
|
||||||
bucket-name: 'redaction'
|
|
||||||
region: 'us-east-1'
|
|
||||||
endpoint: 'https://s3.amazonaws.com'
|
|
||||||
backend: 's3'
|
backend: 's3'
|
||||||
@ -53,6 +53,7 @@ import com.iqser.red.service.redaction.report.v1.server.client.FileStatusClient;
|
|||||||
import com.iqser.red.service.redaction.report.v1.server.client.RedactionLogClient;
|
import com.iqser.red.service.redaction.report.v1.server.client.RedactionLogClient;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.client.ReportTemplateClient;
|
import com.iqser.red.service.redaction.report.v1.server.client.ReportTemplateClient;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.configuration.MessagingConfiguration;
|
import com.iqser.red.service.redaction.report.v1.server.configuration.MessagingConfiguration;
|
||||||
|
import com.iqser.red.service.redaction.report.v1.server.multitenancy.TenantContext;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.service.ReportGenerationService;
|
import com.iqser.red.service.redaction.report.v1.server.service.ReportGenerationService;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.utils.FileSystemBackedStorageService;
|
import com.iqser.red.service.redaction.report.v1.server.utils.FileSystemBackedStorageService;
|
||||||
import com.iqser.red.service.redaction.report.v1.server.utils.MetricValidationUtils;
|
import com.iqser.red.service.redaction.report.v1.server.utils.MetricValidationUtils;
|
||||||
@ -168,7 +169,7 @@ public class RedactionReportV2IntegrationTest {
|
|||||||
when(reportTemplateClient.getReportTemplate("dossierTemplateId", templateId)).thenReturn(testTemplate);
|
when(reportTemplateClient.getReportTemplate("dossierTemplateId", templateId)).thenReturn(testTemplate);
|
||||||
|
|
||||||
ClassPathResource templateResource = new ClassPathResource(template);
|
ClassPathResource templateResource = new ClassPathResource(template);
|
||||||
fileSystemBackedStorageService.storeObject(templateId + "Storage", templateResource.getInputStream());
|
fileSystemBackedStorageService.storeObject(TenantContext.getTenantId(), templateId + "Storage", templateResource.getInputStream());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,14 +193,14 @@ public class RedactionReportV2IntegrationTest {
|
|||||||
|
|
||||||
var id = reportGenerationService.generateReports(reportRequestMessage);
|
var id = reportGenerationService.generateReports(reportRequestMessage);
|
||||||
|
|
||||||
var object = fileSystemBackedStorageService.getObject(id);
|
var object = fileSystemBackedStorageService.getObject(TenantContext.getTenantId(), id);
|
||||||
var infoList = objectMapper.readValue(object.getInputStream(), new TypeReference<List<StoredFileInformation>>() {
|
var infoList = objectMapper.readValue(object.getInputStream(), new TypeReference<List<StoredFileInformation>>() {
|
||||||
});
|
});
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
for (var fileInfo : infoList) {
|
for (var fileInfo : infoList) {
|
||||||
var report = fileSystemBackedStorageService.getObject(fileInfo.getStorageId());
|
var report = fileSystemBackedStorageService.getObject(TenantContext.getTenantId(), fileInfo.getStorageId());
|
||||||
byte[] reportInfo = IOUtils.toByteArray(report.getInputStream());
|
byte[] reportInfo = IOUtils.toByteArray(report.getInputStream());
|
||||||
var outputPath = getTemporaryDirectory() + "/output-" + fileInfo.getTemplateId() + "_" + i + fileEnding;
|
var outputPath = getTemporaryDirectory() + "/output-" + fileInfo.getTemplateId() + "_" + i + fileEnding;
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(outputPath)) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(outputPath)) {
|
||||||
@ -257,7 +258,7 @@ public class RedactionReportV2IntegrationTest {
|
|||||||
|
|
||||||
private List<FileModel> createFileModels(int numOfFileModelsToCreate) {
|
private List<FileModel> createFileModels(int numOfFileModelsToCreate) {
|
||||||
|
|
||||||
return IntStream.range(1, numOfFileModelsToCreate+1)
|
return IntStream.range(1, numOfFileModelsToCreate + 1)
|
||||||
.mapToObj(i -> FileModel.builder().id("fileId" + i).filename("filename" + i).fileAttributes(Map.of("testFileAttribute" + i, "File Attribute Value" + i)).build())
|
.mapToObj(i -> FileModel.builder().id("fileId" + i).filename("filename" + i).fileAttributes(Map.of("testFileAttribute" + i, "File Attribute Value" + i)).build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public class FileSystemBackedStorageService implements StorageService {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public InputStreamResource getObject(String objectId) {
|
public InputStreamResource getObject(String tenantId, String objectId) {
|
||||||
|
|
||||||
var res = dataMap.get(objectId);
|
var res = dataMap.get(objectId);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
@ -44,28 +44,22 @@ public class FileSystemBackedStorageService implements StorageService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteObject(String objectId) {
|
public void deleteObject(String tenantId, String objectId) {
|
||||||
|
|
||||||
dataMap.remove(objectId);
|
dataMap.remove(objectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean objectExists(String objectId) {
|
public boolean objectExists(String tenantId, String objectId) {
|
||||||
|
|
||||||
return dataMap.containsKey(objectId);
|
return dataMap.containsKey(objectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public <T> void storeJSONObject(String objectId, T any) {
|
public <T> void storeJSONObject(String tenantId, String objectId, T any) {
|
||||||
|
|
||||||
File tempFile = File.createTempFile("test", ".tmp");
|
File tempFile = File.createTempFile("test", ".tmp");
|
||||||
getMapper().writeValue(new FileOutputStream(tempFile), any);
|
getMapper().writeValue(new FileOutputStream(tempFile), any);
|
||||||
@ -81,7 +75,7 @@ public class FileSystemBackedStorageService implements StorageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public <T> T readJSONObject(String objectId, Class<T> clazz) {
|
public <T> T readJSONObject(String tenantId, String objectId, Class<T> clazz) {
|
||||||
|
|
||||||
if (dataMap.get(objectId) == null || !dataMap.get(objectId).exists()) {
|
if (dataMap.get(objectId) == null || !dataMap.get(objectId).exists()) {
|
||||||
throw new StorageObjectDoesNotExist("Stored object not found");
|
throw new StorageObjectDoesNotExist("Stored object not found");
|
||||||
@ -104,7 +98,7 @@ public class FileSystemBackedStorageService implements StorageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void storeObject(String objectId, InputStream stream) {
|
public void storeObject(String tenantId, String objectId, InputStream stream) {
|
||||||
|
|
||||||
File tempFile = File.createTempFile("test", ".tmp");
|
File tempFile = File.createTempFile("test", ".tmp");
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user