271 lines
19 KiB
Java
271 lines
19 KiB
Java
package org.opencdmp.service.filetransformer;
|
|
|
|
import org.opencdmp.authorization.AuthorizationFlags;
|
|
import org.opencdmp.authorization.Permission;
|
|
import org.opencdmp.commonmodels.models.FileEnvelopeModel;
|
|
import org.opencdmp.commonmodels.models.description.DescriptionModel;
|
|
import org.opencdmp.commonmodels.models.dmp.DmpModel;
|
|
import org.opencdmp.commons.JsonHandlingService;
|
|
import org.opencdmp.commons.enums.IsActive;
|
|
import org.opencdmp.commons.enums.StorageType;
|
|
import org.opencdmp.commons.enums.TenantConfigurationType;
|
|
import org.opencdmp.commons.scope.tenant.TenantScope;
|
|
import org.opencdmp.commons.types.filetransformer.FileTransformerSourceEntity;
|
|
import org.opencdmp.commons.types.tenantconfiguration.FileTransformerTenantConfigurationEntity;
|
|
import org.opencdmp.convention.ConventionService;
|
|
import org.opencdmp.data.TenantConfigurationEntity;
|
|
import org.opencdmp.event.TenantConfigurationTouchedEvent;
|
|
import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration;
|
|
import org.opencdmp.model.Description;
|
|
import org.opencdmp.model.Dmp;
|
|
import org.opencdmp.model.builder.commonmodels.description.DescriptionCommonModelBuilder;
|
|
import org.opencdmp.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
|
|
import org.opencdmp.model.file.RepositoryFileFormat;
|
|
import org.opencdmp.model.tenantconfiguration.TenantConfiguration;
|
|
import org.opencdmp.query.DescriptionQuery;
|
|
import org.opencdmp.query.DmpQuery;
|
|
import org.opencdmp.query.TenantConfigurationQuery;
|
|
import org.opencdmp.service.encryption.EncryptionService;
|
|
import org.opencdmp.service.storage.StorageFileService;
|
|
import org.opencdmp.service.tenant.TenantProperties;
|
|
import gr.cite.commons.web.authz.service.AuthorizationService;
|
|
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeCacheService;
|
|
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
|
|
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
|
|
import gr.cite.tools.data.builder.BuilderFactory;
|
|
import gr.cite.tools.data.query.QueryFactory;
|
|
import gr.cite.tools.exception.MyNotFoundException;
|
|
import gr.cite.tools.fieldset.BaseFieldSet;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.context.MessageSource;
|
|
import org.springframework.context.event.EventListener;
|
|
import org.springframework.context.i18n.LocaleContextHolder;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
|
import org.springframework.web.reactive.function.client.WebClient;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
import javax.crypto.BadPaddingException;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.NoSuchPaddingException;
|
|
import javax.management.InvalidApplicationException;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.*;
|
|
|
|
@Service
|
|
public class FileTransformerServiceImpl implements FileTransformerService {
|
|
private static final Logger logger = LoggerFactory.getLogger(FileTransformerServiceImpl.class);
|
|
|
|
private final FileTransformerProperties fileTransformerProperties;
|
|
private final Map<String, FileTransformerRepository> clients;
|
|
private final TokenExchangeCacheService tokenExchangeCacheService;
|
|
private final FileTransformerConfigurationCacheService fileTransformerConfigurationCacheService;
|
|
private final AuthorizationService authorizationService;
|
|
private final QueryFactory queryFactory;
|
|
private final BuilderFactory builderFactory;
|
|
private final StorageFileService storageFileService;
|
|
private final MessageSource messageSource;
|
|
private final ConventionService conventionService;
|
|
private final TenantScope tenantScope;
|
|
private final EncryptionService encryptionService;
|
|
private final TenantProperties tenantProperties;
|
|
private final JsonHandlingService jsonHandlingService;
|
|
private final FileTransformerSourcesCacheService fileTransformerSourcesCacheService;
|
|
|
|
@Autowired
|
|
public FileTransformerServiceImpl(FileTransformerProperties fileTransformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCacheService fileTransformerConfigurationCacheService, AuthorizationService authorizationService,
|
|
QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, FileTransformerSourcesCacheService fileTransformerSourcesCacheService) {
|
|
this.fileTransformerProperties = fileTransformerProperties;
|
|
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
|
this.fileTransformerConfigurationCacheService = fileTransformerConfigurationCacheService;
|
|
this.authorizationService = authorizationService;
|
|
this.queryFactory = queryFactory;
|
|
this.builderFactory = builderFactory;
|
|
this.storageFileService = storageFileService;
|
|
this.messageSource = messageSource;
|
|
this.conventionService = conventionService;
|
|
this.tenantScope = tenantScope;
|
|
this.encryptionService = encryptionService;
|
|
this.tenantProperties = tenantProperties;
|
|
this.jsonHandlingService = jsonHandlingService;
|
|
this.fileTransformerSourcesCacheService = fileTransformerSourcesCacheService;
|
|
this.clients = new HashMap<>();
|
|
}
|
|
|
|
private FileTransformerRepository getRepository(String repoId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
String repositoryIdByTenant = this.getRepositoryIdByTenant(repoId);
|
|
if (this.clients.containsKey(repositoryIdByTenant)) return this.clients.get(repositoryIdByTenant);
|
|
|
|
//GK: It's register time
|
|
FileTransformerSourceEntity source = this.getFileTransformerSources().stream().filter(fileTransformerSourceEntity -> fileTransformerSourceEntity.getTransformerId().equals(repoId)).findFirst().orElse(null);
|
|
if (source != null) {
|
|
TokenExchangeModel tokenExchangeModel = new TokenExchangeModel("file_transformer:" + repositoryIdByTenant, source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope());
|
|
TokenExchangeFilterFunction tokenExchangeFilterFunction = new TokenExchangeFilterFunction(this.tokenExchangeCacheService, tokenExchangeModel);
|
|
FileTransformerRepository repository = new FileTransformerRepository(WebClient.builder().baseUrl(source.getUrl() + "/api/file-transformer").filters(exchangeFilterFunctions -> {
|
|
exchangeFilterFunctions.add(tokenExchangeFilterFunction);
|
|
exchangeFilterFunctions.add(logRequest());
|
|
}).build());
|
|
this.clients.put(repositoryIdByTenant, repository);
|
|
return repository;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
|
|
private List<FileTransformerSourceEntity> getFileTransformerSources() throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
String tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
|
|
FileTransformerSourcesCacheService.FileTransformerSourceCacheValue cacheValue = this.fileTransformerSourcesCacheService.lookup(this.fileTransformerSourcesCacheService.buildKey(tenantCode));
|
|
if (cacheValue == null) {
|
|
List<FileTransformerSourceEntity> fileTransformerSourceEntities = new ArrayList<>(this.fileTransformerProperties.getSources());
|
|
if (this.tenantScope.isSet() && this.tenantScope.isMultitenant()) {
|
|
TenantConfigurationQuery tenantConfigurationQuery = this.queryFactory.query(TenantConfigurationQuery.class).isActive(IsActive.Active).types(TenantConfigurationType.FileTransformerPlugins);
|
|
if (this.tenantScope.isDefaultTenant()) tenantConfigurationQuery.tenantIsSet(false);
|
|
else tenantConfigurationQuery.tenantIsSet(true).tenantIds(this.tenantScope.getTenant());
|
|
TenantConfigurationEntity tenantConfiguration = tenantConfigurationQuery.firstAs(new BaseFieldSet().ensure(TenantConfiguration._fileTransformerPlugins));
|
|
|
|
if (tenantConfiguration != null && !this.conventionService.isNullOrEmpty(tenantConfiguration.getValue())) {
|
|
FileTransformerTenantConfigurationEntity fileTransformerTenantConfigurationEntity = this.jsonHandlingService.fromJsonSafe(FileTransformerTenantConfigurationEntity.class, tenantConfiguration.getValue());
|
|
if (fileTransformerTenantConfigurationEntity != null) {
|
|
if (fileTransformerTenantConfigurationEntity.getDisableSystemSources()) fileTransformerSourceEntities = new ArrayList<>();
|
|
fileTransformerSourceEntities.addAll(this.buildFileTransformerSourceItems(fileTransformerTenantConfigurationEntity.getSources()));
|
|
}
|
|
}
|
|
}
|
|
cacheValue = new FileTransformerSourcesCacheService.FileTransformerSourceCacheValue(tenantCode, fileTransformerSourceEntities);
|
|
this.fileTransformerSourcesCacheService.put(cacheValue);
|
|
}
|
|
return cacheValue.getSources();
|
|
}
|
|
|
|
@EventListener
|
|
public void handleTenantConfigurationTouchedEvent(TenantConfigurationTouchedEvent event) {
|
|
if (!event.getType().equals(TenantConfigurationType.FileTransformerPlugins)) return;
|
|
FileTransformerSourcesCacheService.FileTransformerSourceCacheValue fileTransformerSourceCacheValue = this.fileTransformerSourcesCacheService.lookup(this.fileTransformerSourcesCacheService.buildKey(event.getTenantCode()));
|
|
if (fileTransformerSourceCacheValue != null && fileTransformerSourceCacheValue.getSources() != null){
|
|
for (FileTransformerSourceEntity source : fileTransformerSourceCacheValue.getSources()){
|
|
String repositoryIdByTenant = source.getTransformerId() + "_" + event.getTenantCode();
|
|
this.clients.remove(repositoryIdByTenant);
|
|
this.fileTransformerConfigurationCacheService.evict(this.fileTransformerConfigurationCacheService.buildKey(source.getTransformerId(), event.getTenantCode()));
|
|
}
|
|
}
|
|
this.fileTransformerSourcesCacheService.evict(this.fileTransformerSourcesCacheService.buildKey(event.getTenantCode()));
|
|
}
|
|
|
|
private List<FileTransformerSourceEntity> buildFileTransformerSourceItems(List<FileTransformerSourceEntity> sources) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
List<FileTransformerSourceEntity> items = new ArrayList<>();
|
|
if (this.conventionService.isListNullOrEmpty(sources)) return items;
|
|
for (FileTransformerSourceEntity source : sources){
|
|
FileTransformerSourceEntity item = new FileTransformerSourceEntity();
|
|
item.setTransformerId(source.getTransformerId());
|
|
item.setUrl(source.getUrl());
|
|
item.setIssuerUrl(source.getIssuerUrl());
|
|
item.setClientId(source.getClientId());
|
|
if (!this.conventionService.isNullOrEmpty(source.getClientSecret())) item.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), this.tenantProperties.getConfigEncryptionAesKey(), this.tenantProperties.getConfigEncryptionAesIv()));
|
|
item.setScope(source.getScope());
|
|
items.add(item);
|
|
}
|
|
return items;
|
|
}
|
|
|
|
private String getRepositoryIdByTenant(String repositoryId) throws InvalidApplicationException {
|
|
if (this.tenantScope.isSet() && this.tenantScope.isMultitenant()) {
|
|
return repositoryId + "_" + this.tenantScope.getTenantCode();
|
|
} else {
|
|
return repositoryId;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public List<RepositoryFileFormat> getAvailableExportFileFormats() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
List<RepositoryFileFormat> formats = new ArrayList<>();
|
|
List<FileTransformerConfiguration> configurations = this.getAvailableConfigurations();
|
|
for (FileTransformerConfiguration configuration : configurations){
|
|
if (configuration != null && configuration.getExportVariants() != null) formats.addAll(configuration.getExportVariants().stream().map(x-> new RepositoryFileFormat(configuration.getFileTransformerId(), x, configuration.getExportEntityTypes())).toList());
|
|
}
|
|
return formats;
|
|
}
|
|
|
|
private List<FileTransformerConfiguration> getAvailableConfigurations() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
|
|
List<FileTransformerConfiguration> configurations = new ArrayList<>();
|
|
|
|
for (FileTransformerSourceEntity transformerSource : this.getFileTransformerSources()) {
|
|
|
|
String tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
|
|
FileTransformerConfigurationCacheService.FileTransformerConfigurationCacheValue cacheValue = this.fileTransformerConfigurationCacheService.lookup(this.fileTransformerConfigurationCacheService.buildKey(transformerSource.getTransformerId(), tenantCode));
|
|
if (cacheValue == null){
|
|
try {
|
|
FileTransformerRepository fileTransformerRepository = this.getRepository(transformerSource.getTransformerId());
|
|
if (fileTransformerRepository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{transformerSource.getTransformerId(), FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
|
|
|
FileTransformerConfiguration configuration = fileTransformerRepository.getConfiguration();
|
|
cacheValue = new FileTransformerConfigurationCacheService.FileTransformerConfigurationCacheValue(transformerSource.getTransformerId(), tenantCode, configuration);
|
|
this.fileTransformerConfigurationCacheService.put(cacheValue);
|
|
}catch (Exception e){
|
|
logger.error(e.getMessage(), e);
|
|
}
|
|
}
|
|
if (cacheValue != null) {
|
|
configurations.add(cacheValue.getConfiguration());
|
|
}
|
|
}
|
|
|
|
return configurations;
|
|
}
|
|
|
|
@Override
|
|
public org.opencdmp.model.file.FileEnvelope exportDmp(UUID dmpId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
this.authorizationService.authorize(Permission.EditDmp);
|
|
//GK: First get the right client
|
|
FileTransformerRepository repository = this.getRepository(repositoryId);
|
|
if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
|
//GK: Second get the Target Data Management Plan
|
|
DmpQuery query = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpId);
|
|
DmpModel dmpFileTransformerModel = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setRepositoryId(repository.getConfiguration().getFileTransformerId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
|
if (dmpFileTransformerModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
|
|
|
FileEnvelopeModel fileEnvelope = repository.exportDmp(dmpFileTransformerModel, format);
|
|
org.opencdmp.model.file.FileEnvelope result = new org.opencdmp.model.file.FileEnvelope();
|
|
|
|
byte[] data = repository.getConfiguration().isUseSharedStorage() ? this.storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFileRef(), StorageType.Transformer) : fileEnvelope.getFile();
|
|
result.setFile(data);
|
|
result.setFilename(fileEnvelope.getFilename());
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
|
this.authorizationService.authorize(Permission.EditDmp);
|
|
//GK: First get the right client
|
|
FileTransformerRepository repository = this.getRepository(repositoryId);
|
|
if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
|
|
|
//GK: Second get the Target Data Management Plan
|
|
DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionId);
|
|
DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getFileTransformerId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
|
if (descriptionFileTransformerModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
|
|
|
FileEnvelopeModel fileEnvelope = repository.exportDescription(descriptionFileTransformerModel, format);
|
|
org.opencdmp.model.file.FileEnvelope result = new org.opencdmp.model.file.FileEnvelope();
|
|
byte[] data = repository.getConfiguration().isUseSharedStorage() ? this.storageFileService.readByFileRefAsBytesSafe(fileEnvelope.getFileRef(), StorageType.Transformer) : fileEnvelope.getFile(); //TODO: shared storage should be per repository
|
|
result.setFile(data);
|
|
result.setFilename(fileEnvelope.getFilename());
|
|
return result;
|
|
}
|
|
|
|
// This method returns filter function which will log request data
|
|
private static ExchangeFilterFunction logRequest() {
|
|
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
|
|
logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
|
|
clientRequest.headers().forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
|
|
return Mono.just(clientRequest);
|
|
});
|
|
}
|
|
}
|