package eu.eudat.service.tenantconfiguration; import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.StorageType; import eu.eudat.commons.enums.TenantConfigurationType; import eu.eudat.commons.types.deposit.DepositSourceEntity; import eu.eudat.commons.types.filetransformer.FileTransformerSourceEntity; import eu.eudat.commons.types.tenantconfiguration.*; import eu.eudat.convention.ConventionService; import eu.eudat.data.TenantConfigurationEntity; import eu.eudat.data.TenantEntityManager; import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.model.StorageFile; import eu.eudat.model.builder.tenantconfiguration.TenantConfigurationBuilder; import eu.eudat.model.deleter.TenantConfigurationDeleter; import eu.eudat.model.persist.deposit.DepositSourcePersist; import eu.eudat.model.persist.filetransformer.FileTransformerSourcePersist; import eu.eudat.model.persist.tenantconfiguration.*; import eu.eudat.model.tenantconfiguration.TenantConfiguration; import eu.eudat.service.encryption.EncryptionService; import eu.eudat.service.storage.StorageFileService; import eu.eudat.service.tenant.TenantProperties; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; 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.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.UUID; @Service public class TenantConfigurationServiceImpl implements TenantConfigurationService { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigurationServiceImpl.class)); private final TenantEntityManager entityManager; private final AuthorizationService authorizationService; private final DeleterFactory deleterFactory; private final BuilderFactory builderFactory; private final ConventionService conventionService; private final ErrorThesaurusProperties errors; private final MessageSource messageSource; private final JsonHandlingService jsonHandlingService; private final EncryptionService encryptionService; private final TenantProperties tenantProperties; private final StorageFileService storageFileService; @Autowired public TenantConfigurationServiceImpl( TenantEntityManager entityManager, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, JsonHandlingService jsonHandlingService, EncryptionService encryptionService, TenantProperties tenantProperties, StorageFileService storageFileService) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; this.builderFactory = builderFactory; this.conventionService = conventionService; this.errors = errors; this.messageSource = messageSource; this.jsonHandlingService = jsonHandlingService; this.encryptionService = encryptionService; this.tenantProperties = tenantProperties; this.storageFileService = storageFileService; } public TenantConfiguration persist(TenantConfigurationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("persisting data TenantConfiguration").And("model", model).And("fields", fields)); this.authorizationService.authorizeForce(Permission.EditTenantConfiguration); Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); TenantConfigurationEntity data; if (isUpdate) { data = this.entityManager.find(TenantConfigurationEntity.class, model.getId()); if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); if (!data.getType().equals(model.getType())) throw new MyValidationException(this.errors.getTenantConfigurationTypeCanNotChange().getCode(), this.errors.getTenantConfigurationTypeCanNotChange().getMessage()); } else { data = new TenantConfigurationEntity(); data.setId(UUID.randomUUID()); data.setIsActive(IsActive.Active); data.setCreatedAt(Instant.now()); data.setType(model.getType()); } switch (data.getType()){ case CssColors -> data.setValue(this.jsonHandlingService.toJson(this.buildCssColorsTenantConfigurationEntity(model.getCssColors()))); case DefaultUserLocale -> data.setValue(this.jsonHandlingService.toJson(this.buildDefaultUserLocaleTenantConfigurationEntity(model.getDefaultUserLocale()))); case DepositPlugins -> data.setValue(this.jsonHandlingService.toJson(this.buildDepositTenantConfigurationEntity(model.getDepositPlugins()))); case FileTransformerPlugins -> data.setValue(this.jsonHandlingService.toJson(this.buildFileTransformerTenantConfigurationEntity(model.getFileTransformerPlugins()))); case Logo -> { LogoTenantConfigurationEntity oldValue = this.conventionService.isNullOrEmpty(data.getValue()) ? null : this.jsonHandlingService.fromJsonSafe(LogoTenantConfigurationEntity.class, data.getValue()); data.setValue(this.jsonHandlingService.toJson(this.buildLogoTenantConfigurationEntity(model.getLogo(), oldValue))); } default -> throw new InternalError("unknown type: " + data.getType()); } data.setUpdatedAt(Instant.now()); if (isUpdate) this.entityManager.merge(data); else this.entityManager.persist(data); this.entityManager.flush(); return this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, TenantConfiguration._id), data); } private @NotNull DepositTenantConfigurationEntity buildDepositTenantConfigurationEntity(DepositTenantConfigurationPersist persist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { DepositTenantConfigurationEntity data = new DepositTenantConfigurationEntity(); if (persist == null || this.conventionService.isListNullOrEmpty(persist.getSources())) return data; data.setSources(new ArrayList<>()); for (DepositSourcePersist depositSourcePersist : persist.getSources()) { data.getSources().add(this.buildDepositSourceEntity(depositSourcePersist)); } return data; } private DepositSourceEntity buildDepositSourceEntity(DepositSourcePersist depositSourcePersist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { DepositSourceEntity depositSourceEntity = new DepositSourceEntity(); depositSourceEntity.setClientId(depositSourcePersist.getClientId()); if (!this.conventionService.isNullOrEmpty(depositSourcePersist.getClientSecret())) depositSourceEntity.setClientSecret(this.encryptionService.encryptAES(depositSourcePersist.getClientSecret(), this.tenantProperties.getConfigEncryptionAesKey(), this.tenantProperties.getConfigEncryptionAesIv())); depositSourceEntity.setRepositoryId(depositSourcePersist.getRepositoryId()); depositSourceEntity.setUrl(depositSourcePersist.getUrl()); depositSourceEntity.setIssuerUrl(depositSourcePersist.getIssuerUrl()); depositSourceEntity.setScope(depositSourcePersist.getScope()); depositSourceEntity.setPdfTransformerId(depositSourcePersist.getPdfTransformerId()); depositSourceEntity.setRdaTransformerId(depositSourcePersist.getRdaTransformerId()); return depositSourceEntity; } private @NotNull FileTransformerTenantConfigurationEntity buildFileTransformerTenantConfigurationEntity(FileTransformerTenantConfigurationPersist persist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { FileTransformerTenantConfigurationEntity data = new FileTransformerTenantConfigurationEntity(); if (persist == null || this.conventionService.isListNullOrEmpty(persist.getSources())) return data; data.setSources(new ArrayList<>()); for (FileTransformerSourcePersist depositSourcePersist : persist.getSources()) { data.getSources().add(this.buildFileTransformerSourceEntity(depositSourcePersist)); } return data; } private FileTransformerSourceEntity buildFileTransformerSourceEntity(FileTransformerSourcePersist depositSourcePersist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { FileTransformerSourceEntity depositSourceEntity = new FileTransformerSourceEntity(); depositSourceEntity.setClientId(depositSourcePersist.getClientId()); if (!this.conventionService.isNullOrEmpty(depositSourcePersist.getClientSecret())) depositSourceEntity.setClientSecret(this.encryptionService.encryptAES(depositSourcePersist.getClientSecret(), this.tenantProperties.getConfigEncryptionAesKey(), this.tenantProperties.getConfigEncryptionAesIv())); depositSourceEntity.setUrl(depositSourcePersist.getUrl()); depositSourceEntity.setIssuerUrl(depositSourcePersist.getIssuerUrl()); depositSourceEntity.setScope(depositSourcePersist.getScope()); depositSourceEntity.setTransformerId(depositSourcePersist.getTransformerId()); return depositSourceEntity; } private @NotNull CssColorsTenantConfigurationEntity buildCssColorsTenantConfigurationEntity(CssColorsTenantConfigurationPersist persist){ CssColorsTenantConfigurationEntity data = new CssColorsTenantConfigurationEntity(); if (persist == null) return data; data.setPrimaryColor(persist.getPrimaryColor()); data.setPrimaryColor2(persist.getPrimaryColor2()); data.setPrimaryColor3(persist.getPrimaryColor3()); data.setSecondaryColor(persist.getSecondaryColor()); return data; } private @NotNull DefaultUserLocaleTenantConfigurationEntity buildDefaultUserLocaleTenantConfigurationEntity(DefaultUserLocaleTenantConfigurationPersist persist){ DefaultUserLocaleTenantConfigurationEntity data = new DefaultUserLocaleTenantConfigurationEntity(); if (persist == null) return data; data.setCulture(persist.getCulture()); data.setLanguage(persist.getLanguage()); data.setTimezone(persist.getTimezone()); return data; } private @NotNull LogoTenantConfigurationEntity buildLogoTenantConfigurationEntity(LogoTenantConfigurationPersist persist, LogoTenantConfigurationEntity oldValue) throws InvalidApplicationException { LogoTenantConfigurationEntity data = new LogoTenantConfigurationEntity(); if (persist == null) return data; data.setStorageFileId(persist.getStorageFileId()); UUID existingFileId = oldValue != null ? oldValue.getStorageFileId() : null; if (persist.getStorageFileId() != null){ if (!persist.getStorageFileId().equals(existingFileId)) { StorageFile storageFile = this.storageFileService.copyToStorage(persist.getStorageFileId(), StorageType.Main, true, new BaseFieldSet().ensure(StorageFile._id)); this.storageFileService.updatePurgeAt(storageFile.getId(), null); if (existingFileId != null) this.storageFileService.updatePurgeAt(existingFileId, Instant.now().minusSeconds(60)); data.setStorageFileId(storageFile.getId()); } else { data.setStorageFileId(existingFileId); } } else { if (existingFileId != null) this.storageFileService.updatePurgeAt(existingFileId, Instant.now().minusSeconds(60)); data.setStorageFileId(null); } return data; } public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting dataset: {}", id); this.authorizationService.authorizeForce(Permission.DeleteTenantConfiguration); TenantConfigurationEntity data = this.entityManager.find(TenantConfigurationEntity.class, id); if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (data.getType().equals(TenantConfigurationType.Logo)){ LogoTenantConfigurationEntity oldValue = this.conventionService.isNullOrEmpty(data.getValue()) ? null : this.jsonHandlingService.fromJsonSafe(LogoTenantConfigurationEntity.class, data.getValue()); if (oldValue != null && oldValue.getStorageFileId() != null) this.storageFileService.updatePurgeAt(oldValue.getStorageFileId(), Instant.now().minusSeconds(60)); } this.deleterFactory.deleter(TenantConfigurationDeleter.class).deleteAndSaveByIds(List.of(id)); } }