diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantConfigEntity.java b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantConfigEntity.java new file mode 100644 index 000000000..e1f092038 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantConfigEntity.java @@ -0,0 +1,34 @@ +package eu.eudat.commons.types.tenant; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "config") +@XmlAccessorType(XmlAccessType.FIELD) +public class TenantConfigEntity { + + @XmlElement(name = "deposit-configuration") + private TenantDepositConfigEntity deposit; + + @XmlElement(name = "file-transformers-configuration") + private TenantFileTransformersConfigEntity fileTransformers; + + public TenantDepositConfigEntity getDeposit() { + return deposit; + } + + public void setDeposit(TenantDepositConfigEntity deposit) { + this.deposit = deposit; + } + + public TenantFileTransformersConfigEntity getFileTransformers() { + return fileTransformers; + } + + public void setFileTransformers(TenantFileTransformersConfigEntity fileTransformers) { + this.fileTransformers = fileTransformers; + } +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantDepositConfigEntity.java b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantDepositConfigEntity.java new file mode 100644 index 000000000..cb1bc3073 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantDepositConfigEntity.java @@ -0,0 +1,25 @@ +package eu.eudat.commons.types.tenant; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; + +import java.util.List; + +@XmlAccessorType(XmlAccessType.FIELD) +public class TenantDepositConfigEntity { + + @XmlElementWrapper(name = "sources") + @XmlElement(name = "source") + private List sources; + + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantFileTransformersConfigEntity.java b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantFileTransformersConfigEntity.java new file mode 100644 index 000000000..842735b2e --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantFileTransformersConfigEntity.java @@ -0,0 +1,24 @@ +package eu.eudat.commons.types.tenant; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; + +import java.util.List; + +@XmlAccessorType(XmlAccessType.FIELD) +public class TenantFileTransformersConfigEntity { + + @XmlElementWrapper(name = "sources") + @XmlElement(name = "source") + private List sources; + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantSourceEntity.java b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantSourceEntity.java new file mode 100644 index 000000000..33b335147 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/types/tenant/TenantSourceEntity.java @@ -0,0 +1,79 @@ +package eu.eudat.commons.types.tenant; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; + +import java.util.List; + +@XmlAccessorType(XmlAccessType.FIELD) +public class TenantSourceEntity { + + @XmlElement(name = "url") + private String url; + + @XmlElementWrapper(name = "codes") + @XmlElement(name = "code") + private List codes; + + @XmlElement(name = "issuer-url") + private String issuerUrl; + + @XmlElement(name = "client-id") + private String clientId; + + @XmlElement(name = "client-secret") + private String clientSecret; + + @XmlElement(name = "scope") + private String scope; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getCodes() { + return codes; + } + + public void setCodes(List codes) { + this.codes = codes; + } + + public String getIssuerUrl() { + return issuerUrl; + } + + public void setIssuerUrl(String issuerUrl) { + this.issuerUrl = issuerUrl; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantConfiguration.java b/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantConfiguration.java new file mode 100644 index 000000000..e91e3367a --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantConfiguration.java @@ -0,0 +1,20 @@ +package eu.eudat.configurations.tenant; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(TenantProperties.class) +public class TenantConfiguration { + private final TenantProperties properties; + + @Autowired + public TenantConfiguration(TenantProperties properties) { + this.properties = properties; + } + + public TenantProperties getProperties() { + return properties; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantProperties.java b/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantProperties.java new file mode 100644 index 000000000..a64b51a6c --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/configurations/tenant/TenantProperties.java @@ -0,0 +1,26 @@ +package eu.eudat.configurations.tenant; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "tenant") +public class TenantProperties { + + private String configEncryptionAesKey; + private String configEncryptionAesIv; + + public String getConfigEncryptionAesKey() { + return configEncryptionAesKey; + } + + public void setConfigEncryptionAesKey(String configEncryptionAesKey) { + this.configEncryptionAesKey = configEncryptionAesKey; + } + + public String getConfigEncryptionAesIv() { + return configEncryptionAesIv; + } + + public void setConfigEncryptionAesIv(String configEncryptionAesIv) { + this.configEncryptionAesIv = configEncryptionAesIv; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/Tenant.java b/dmp-backend/core/src/main/java/eu/eudat/model/Tenant.java index 46b589b72..7c7f6f840 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/Tenant.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/Tenant.java @@ -1,6 +1,7 @@ package eu.eudat.model; import eu.eudat.commons.enums.IsActive; +import eu.eudat.model.tenantconfig.TenantConfig; import java.time.Instant; import java.util.UUID; @@ -23,7 +24,7 @@ public class Tenant { private IsActive isActive; public final static String _isActive = "isActive"; - private String config; + private TenantConfig config; public final static String _config = "config"; private Instant createdAt; @@ -74,11 +75,11 @@ public class Tenant { this.isActive = isActive; } - public String getConfig() { + public TenantConfig getConfig() { return config; } - public void setConfig(String config) { + public void setConfig(TenantConfig config) { this.config = config; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/TenantBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/TenantBuilder.java index 6475bc955..b2cd292cc 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/TenantBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/TenantBuilder.java @@ -1,9 +1,13 @@ package eu.eudat.model.builder; import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.XmlHandlingService; +import eu.eudat.commons.types.tenant.TenantConfigEntity; import eu.eudat.convention.ConventionService; import eu.eudat.data.TenantEntity; import eu.eudat.model.Tenant; +import eu.eudat.model.builder.tenantconfig.TenantConfigBuilder; +import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.DataLogEntry; @@ -20,10 +24,14 @@ import java.util.*; @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class TenantBuilder extends BaseBuilder { + private final BuilderFactory builderFactory; + private final XmlHandlingService xmlHandlingService; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); @Autowired - public TenantBuilder(ConventionService conventionService) { + public TenantBuilder(ConventionService conventionService, BuilderFactory builderFactory, XmlHandlingService xmlHandlingService) { super(conventionService, new LoggerService(LoggerFactory.getLogger(TenantBuilder.class))); + this.builderFactory = builderFactory; + this.xmlHandlingService = xmlHandlingService; } public TenantBuilder authorize(EnumSet values){ @@ -36,6 +44,8 @@ public class TenantBuilder extends BaseBuilder { this.logger.trace(new DataLogEntry("requested fields",fields)); if(fields == null || data == null || fields.isEmpty()) return new ArrayList<>(); + FieldSet configFields = fields.extractPrefixed(this.asPrefix(Tenant._config)); + List models = new ArrayList<>(); for(TenantEntity d : data){ Tenant m = new Tenant(); @@ -44,7 +54,10 @@ public class TenantBuilder extends BaseBuilder { if(fields.hasField(this.asIndexer(Tenant._name))) m.setName(d.getName()); if(fields.hasField(this.asIndexer(Tenant._description))) m.setDescription(d.getDescription()); if(fields.hasField(this.asIndexer(Tenant._isActive))) m.setIsActive(d.getIsActive()); - if(fields.hasField(this.asIndexer(Tenant._config))) m.setConfig(d.getConfig()); + if (!configFields.isEmpty() && d.getConfig() != null){ + TenantConfigEntity config = this.xmlHandlingService.fromXmlSafe(TenantConfigEntity.class, d.getConfig()); + m.setConfig(this.builderFactory.builder(TenantConfigBuilder.class).authorize(this.authorize).build(configFields, config)); + } if(fields.hasField(this.asIndexer(Tenant._createdAt))) m.setCreatedAt(d.getCreatedAt()); if(fields.hasField(this.asIndexer(Tenant._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); if(fields.hasField(this.asIndexer(Tenant._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantConfigBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantConfigBuilder.java new file mode 100644 index 000000000..7b9ddd38a --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantConfigBuilder.java @@ -0,0 +1,60 @@ +package eu.eudat.model.builder.tenantconfig; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.types.tenant.TenantConfigEntity; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.builder.BaseBuilder; +import eu.eudat.model.tenantconfig.TenantConfig; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantConfigBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public TenantConfigBuilder( + ConventionService conventionService, BuilderFactory builderFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(TenantConfigBuilder.class))); + this.builderFactory = builderFactory; + } + + public TenantConfigBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet depositFields = fields.extractPrefixed(this.asPrefix(TenantConfig._deposit)); + FieldSet fileFields = fields.extractPrefixed(this.asPrefix(TenantConfig._fileTransformers)); + + List models = new ArrayList<>(); + for (TenantConfigEntity d : data) { + TenantConfig m = new TenantConfig(); + if (!depositFields.isEmpty() && d.getDeposit() != null) m.setDeposit(this.builderFactory.builder(TenantDepositConfigBuilder.class).authorize(this.authorize).build(depositFields, d.getDeposit())); + if (!fileFields.isEmpty() && d.getFileTransformers() != null) m.setFileTransformers(this.builderFactory.builder(TenantFileTransformersBuilder.class).authorize(this.authorize).build(fileFields, d.getFileTransformers())); + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + return models; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantDepositConfigBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantDepositConfigBuilder.java new file mode 100644 index 000000000..49531b6a2 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantDepositConfigBuilder.java @@ -0,0 +1,61 @@ +package eu.eudat.model.builder.tenantconfig; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.types.tenant.TenantDepositConfigEntity; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.builder.BaseBuilder; +import eu.eudat.model.tenantconfig.TenantDepositConfig; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantDepositConfigBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public TenantDepositConfigBuilder( + ConventionService conventionService, BuilderFactory builderFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(TenantDepositConfigBuilder.class))); + this.builderFactory = builderFactory; + } + + public TenantDepositConfigBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet sourcesFields = fields.extractPrefixed(this.asPrefix(TenantDepositConfig._sources)); + + List models = new ArrayList<>(); + for (TenantDepositConfigEntity d : data) { + TenantDepositConfig m = new TenantDepositConfig(); + if (!sourcesFields.isEmpty() && d.getSources() != null) { + m.setSources(this.builderFactory.builder(TenantSourceBuilder.class).authorize(this.authorize).build(sourcesFields, d.getSources())); + } + + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + return models; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantFileTransformersBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantFileTransformersBuilder.java new file mode 100644 index 000000000..660b0b6ec --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantFileTransformersBuilder.java @@ -0,0 +1,61 @@ +package eu.eudat.model.builder.tenantconfig; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.types.tenant.TenantFileTransformersConfigEntity; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.builder.BaseBuilder; +import eu.eudat.model.tenantconfig.TenantFileTransformersConfig; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantFileTransformersBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public TenantFileTransformersBuilder( + ConventionService conventionService, BuilderFactory builderFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(TenantFileTransformersBuilder.class))); + this.builderFactory = builderFactory; + } + + public TenantFileTransformersBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet sourcesFields = fields.extractPrefixed(this.asPrefix(TenantFileTransformersConfig._sources)); + + List models = new ArrayList<>(); + for (TenantFileTransformersConfigEntity d : data) { + TenantFileTransformersConfig m = new TenantFileTransformersConfig(); + if (!sourcesFields.isEmpty() && d.getSources() != null) { + m.setSources(this.builderFactory.builder(TenantSourceBuilder.class).authorize(this.authorize).build(sourcesFields, d.getSources())); + } + + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + return models; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantSourceBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantSourceBuilder.java new file mode 100644 index 000000000..ce68edb25 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/tenantconfig/TenantSourceBuilder.java @@ -0,0 +1,63 @@ +package eu.eudat.model.builder.tenantconfig; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.types.tenant.TenantSourceEntity; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.builder.BaseBuilder; +import eu.eudat.model.tenantconfig.TenantSource; +import eu.eudat.service.tenant.TenantService; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantSourceBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public TenantSourceBuilder( + ConventionService conventionService, BuilderFactory builderFactory, TenantService tenantService) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(TenantSourceBuilder.class))); + this.builderFactory = builderFactory; + } + + public TenantSourceBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + List models = new ArrayList<>(); + for (TenantSourceEntity d : data) { + TenantSource m = new TenantSource(); + if (fields.hasField(this.asIndexer(TenantSource._url))) m.setUrl(d.getUrl()); + if (fields.hasField(this.asIndexer(TenantSource._codes))) m.setCodes(d.getCodes()); + if (fields.hasField(this.asIndexer(TenantSource._issuerUrl))) m.setIssuerUrl(d.getIssuerUrl()); + if (fields.hasField(this.asIndexer(TenantSource._clientId))) m.setClientId(d.getClientId()); + if (fields.hasField(this.asIndexer(TenantSource._clientSecret))) m.setClientSecret(d.getClientSecret()); + if (fields.hasField(this.asIndexer(TenantSource._scope))) m.setScope(d.getScope()); + + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + return models; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantConfigCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantConfigCensor.java new file mode 100644 index 000000000..144eef5d7 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantConfigCensor.java @@ -0,0 +1,51 @@ +package eu.eudat.model.censorship.tenantconfig; + +import eu.eudat.authorization.Permission; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.censorship.BaseCensor; +import eu.eudat.model.censorship.referencetypedefinition.ReferenceTypeFieldCensor; +import eu.eudat.model.censorship.referencetypedefinition.ReferenceTypeSourceBaseConfigurationCensor; +import eu.eudat.model.tenantconfig.TenantConfig; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantConfigCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public TenantConfigCensor(ConventionService conventionService, + AuthorizationService authService, + CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowseTenant); + FieldSet depositFields = fields.extractPrefixed(this.asIndexerPrefix(TenantConfig._deposit)); + this.censorFactory.censor(TenantDepositConfigCensor.class).censor(depositFields, userId); + + FieldSet fileFields = fields.extractPrefixed(this.asIndexerPrefix(TenantConfig._fileTransformers)); + this.censorFactory.censor(TenantFileTransformersConfigCensor.class).censor(fileFields, userId); + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantDepositConfigCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantDepositConfigCensor.java new file mode 100644 index 000000000..a1c0593dc --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantDepositConfigCensor.java @@ -0,0 +1,48 @@ +package eu.eudat.model.censorship.tenantconfig; + +import eu.eudat.authorization.Permission; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.censorship.BaseCensor; +import eu.eudat.model.censorship.referencetypedefinition.ReferenceTypeFieldCensor; +import eu.eudat.model.tenantconfig.TenantDepositConfig; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantDepositConfigCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDepositConfigCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public TenantDepositConfigCensor(ConventionService conventionService, + AuthorizationService authService, + CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowseTenant); + FieldSet sourceFields = fields.extractPrefixed(this.asIndexerPrefix(TenantDepositConfig._sources)); + this.censorFactory.censor(TenantSourceCensor.class).censor(sourceFields, userId); + + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantFileTransformersConfigCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantFileTransformersConfigCensor.java new file mode 100644 index 000000000..78c71b8d8 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantFileTransformersConfigCensor.java @@ -0,0 +1,47 @@ +package eu.eudat.model.censorship.tenantconfig; + +import eu.eudat.authorization.Permission; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.censorship.BaseCensor; +import eu.eudat.model.tenantconfig.TenantFileTransformersConfig; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantFileTransformersConfigCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantFileTransformersConfigCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public TenantFileTransformersConfigCensor(ConventionService conventionService, + AuthorizationService authService, + CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowseTenant); + FieldSet sourceFields = fields.extractPrefixed(this.asIndexerPrefix(TenantFileTransformersConfig._sources)); + this.censorFactory.censor(TenantSourceCensor.class).censor(sourceFields, userId); + + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantSourceCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantSourceCensor.java new file mode 100644 index 000000000..e81ecf31b --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/tenantconfig/TenantSourceCensor.java @@ -0,0 +1,43 @@ +package eu.eudat.model.censorship.tenantconfig; + +import eu.eudat.authorization.Permission; +import eu.eudat.convention.ConventionService; +import eu.eudat.model.censorship.BaseCensor; +import eu.eudat.model.tenantconfig.TenantSource; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantSourceCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantSourceCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public TenantSourceCensor(ConventionService conventionService, + AuthorizationService authService, CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowseTenant); + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/TenantPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/TenantPersist.java index e90cd934d..e9ddab198 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/TenantPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/TenantPersist.java @@ -2,6 +2,7 @@ package eu.eudat.model.persist; import eu.eudat.commons.validation.FieldNotNullIfOtherSet; import eu.eudat.commons.validation.ValidId; +import eu.eudat.model.persist.tenantconfig.TenantConfigPersist; import jakarta.validation.constraints.*; import java.util.UUID; @@ -26,7 +27,7 @@ public class TenantPersist { @NotEmpty(message = "{validation.empty}") private String description; - private String config; + private TenantConfigPersist config; private String hash; @@ -62,11 +63,11 @@ public class TenantPersist { this.description = description; } - public String getConfig() { + public TenantConfigPersist getConfig() { return config; } - public void setConfig(String config) { + public void setConfig(TenantConfigPersist config) { this.config = config; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantConfigPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantConfigPersist.java new file mode 100644 index 000000000..970d34615 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantConfigPersist.java @@ -0,0 +1,24 @@ +package eu.eudat.model.persist.tenantconfig; + + +public class TenantConfigPersist { + private TenantDepositConfigPersist deposit; + private TenantFileTransformersConfigPersist fileTransformers; + + public TenantDepositConfigPersist getDeposit() { + return deposit; + } + + public void setDeposit(TenantDepositConfigPersist deposit) { + this.deposit = deposit; + } + + public TenantFileTransformersConfigPersist getFileTransformers() { + return fileTransformers; + } + + public void setFileTransformers(TenantFileTransformersConfigPersist fileTransformers) { + this.fileTransformers = fileTransformers; + } +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java new file mode 100644 index 000000000..7b286e840 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java @@ -0,0 +1,20 @@ +package eu.eudat.model.persist.tenantconfig; + + +import jakarta.validation.Valid; + +import java.util.List; + +public class TenantDepositConfigPersist { + + @Valid + private List sources; + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java new file mode 100644 index 000000000..3701120d1 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java @@ -0,0 +1,20 @@ +package eu.eudat.model.persist.tenantconfig; + + +import jakarta.validation.Valid; + +import java.util.List; + +public class TenantFileTransformersConfigPersist { + + @Valid + private List sources; + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java new file mode 100644 index 000000000..f7d6a2cc1 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java @@ -0,0 +1,82 @@ +package eu.eudat.model.persist.tenantconfig; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import java.util.List; + +public class TenantSourcePersist { + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private String url; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private List codes; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private String issuerUrl ; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private String clientId; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private String clientSecret; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + private String scope; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getCodes() { + return codes; + } + + public void setCodes(List codes) { + this.codes = codes; + } + + public String getIssuerUrl() { + return issuerUrl; + } + + public void setIssuerUrl(String issuerUrl) { + this.issuerUrl = issuerUrl; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantConfig.java b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantConfig.java new file mode 100644 index 000000000..969103efe --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantConfig.java @@ -0,0 +1,28 @@ +package eu.eudat.model.tenantconfig; + + +public class TenantConfig { + + public final static String _deposit = "deposit"; + private TenantDepositConfig deposit; + + public final static String _fileTransformers = "fileTransformers"; + private TenantFileTransformersConfig fileTransformers; + + public TenantDepositConfig getDeposit() { + return deposit; + } + + public void setDeposit(TenantDepositConfig deposit) { + this.deposit = deposit; + } + + public TenantFileTransformersConfig getFileTransformers() { + return fileTransformers; + } + + public void setFileTransformers(TenantFileTransformersConfig fileTransformers) { + this.fileTransformers = fileTransformers; + } +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantDepositConfig.java b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantDepositConfig.java new file mode 100644 index 000000000..a8a90225c --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantDepositConfig.java @@ -0,0 +1,17 @@ +package eu.eudat.model.tenantconfig; + +import java.util.List; + +public class TenantDepositConfig { + + public final static String _sources = "sources"; + private List sources; + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantFileTransformersConfig.java b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantFileTransformersConfig.java new file mode 100644 index 000000000..1c853914b --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantFileTransformersConfig.java @@ -0,0 +1,17 @@ +package eu.eudat.model.tenantconfig; + +import java.util.List; + +public class TenantFileTransformersConfig { + + public final static String _sources = "sources"; + private List sources; + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantSource.java b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantSource.java new file mode 100644 index 000000000..725d33759 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/tenantconfig/TenantSource.java @@ -0,0 +1,73 @@ +package eu.eudat.model.tenantconfig; + + +import java.util.List; + +public class TenantSource { + + public final static String _url = "url"; + private String url; + + public final static String _codes = "codes"; + private List codes; + + public final static String _issuerUrl = "issuerUrl"; + private String issuerUrl; + + public final static String _clientId = "clientId"; + private String clientId; + + public final static String _clientSecret = "clientSecret"; + private String clientSecret; + + public final static String _scope = "scope"; + private String scope; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getCodes() { + return codes; + } + + public void setCodes(List codes) { + this.codes = codes; + } + + public String getIssuerUrl() { + return issuerUrl; + } + + public void setIssuerUrl(String issuerUrl) { + this.issuerUrl = issuerUrl; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DmpDescriptionTemplateQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DmpDescriptionTemplateQuery.java index eaf4c5ee4..0f8a5c9f9 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DmpDescriptionTemplateQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DmpDescriptionTemplateQuery.java @@ -38,6 +38,8 @@ public class DmpDescriptionTemplateQuery extends QueryBase descriptionTemplateGroupIds; + private Collection sectionIds; + private Collection isActives; private Collection excludedIds; @@ -90,18 +92,18 @@ public class DmpDescriptionTemplateQuery extends QueryBase values) { - this.dmpIds = values; + public DmpDescriptionTemplateQuery sectionIds(Collection values) { + this.sectionIds = values; return this; } @@ -120,6 +122,21 @@ public class DmpDescriptionTemplateQuery extends QueryBase values) { + this.dmpIds = values; + return this; + } + public DmpDescriptionTemplateQuery authorize(EnumSet values) { this.authorize = values; return this; @@ -193,6 +210,12 @@ public class DmpDescriptionTemplateQuery extends QueryBase inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpDescriptionTemplateEntity._sectionId)); + for (UUID item : this.sectionIds) + inClause.value(item); + predicates.add(inClause); + } if (this.isActives != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpDescriptionTemplateEntity._isActive)); for (IsActive item : this.isActives) diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java index 90e81fbf7..86477cd29 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java @@ -151,7 +151,7 @@ public class TenantQuery extends QueryBase { else if (item.match(Tenant._code)) return TenantEntity._code; else if (item.match(Tenant._name)) return TenantEntity._name; else if (item.match(Tenant._description)) return TenantEntity._description; - else if (item.match(Tenant._config)) return TenantEntity._config; + else if (item.prefix(Tenant._config)) return TenantEntity._config; else if (item.match(Tenant._createdAt)) return TenantEntity._createdAt; else if (item.match(Tenant._updatedAt)) return TenantEntity._updatedAt; else if (item.match(Tenant._isActive)) return TenantEntity._isActive; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionService.java b/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionService.java new file mode 100644 index 000000000..064b8b144 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionService.java @@ -0,0 +1,14 @@ +package eu.eudat.service.encryption; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public interface EncryptionService { + + String encryptAES(String input, String key, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException; + String decryptAES(String cipherText, String key, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException; +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionServiceImpl.java new file mode 100644 index 000000000..703191e64 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/service/encryption/EncryptionServiceImpl.java @@ -0,0 +1,62 @@ +package eu.eudat.service.encryption; + +import eu.eudat.convention.ConventionService; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +@Service +public class EncryptionServiceImpl implements EncryptionService { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EncryptionServiceImpl.class)); + private final AuthorizationService authorizationService; + + private final ConventionService conventionService; + @Autowired + public EncryptionServiceImpl( + AuthorizationService authorizationService, + ConventionService conventionService) { + this.authorizationService = authorizationService; + this.conventionService = conventionService; + } + + @Override + public String encryptAES(String input, String keyString, String ivString) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + + SecretKey key = new SecretKeySpec(keyString.getBytes(),"AES"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(ivString.getBytes()); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec ); + byte[] cipherText = cipher.doFinal(input.getBytes()); + return Base64.getEncoder() + .encodeToString(cipherText); + } + + @Override + public String decryptAES(String cipherText, String keyString, String ivString) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + + SecretKey key = new SecretKeySpec(keyString.getBytes(),"AES"); + IvParameterSpec iv = new IvParameterSpec(ivString.getBytes()); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + byte[] plainText = cipher.doFinal(Base64.getDecoder() + .decode(cipherText)); + return new String(plainText); + } + +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java index c3a4650a0..2aa543af0 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java @@ -8,12 +8,23 @@ import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; import javax.management.InvalidApplicationException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; import java.util.UUID; public interface TenantService { - Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; + Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, + InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; + Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java index 38ddfb31f..8d14c33b3 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java @@ -4,6 +4,8 @@ import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.commons.XmlHandlingService; import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.types.tenant.*; +import eu.eudat.configurations.tenant.TenantProperties; import eu.eudat.convention.ConventionService; import eu.eudat.data.TenantEntity; import eu.eudat.errorcode.ErrorThesaurusProperties; @@ -11,6 +13,9 @@ import eu.eudat.model.Tenant; import eu.eudat.model.builder.TenantBuilder; import eu.eudat.model.deleter.TenantDeleter; import eu.eudat.model.persist.TenantPersist; +import eu.eudat.model.persist.tenantconfig.*; +import eu.eudat.model.tenantconfig.TenantSource; +import eu.eudat.service.encryption.EncryptionService; import eu.eudat.service.responseutils.ResponseUtilsService; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; @@ -26,16 +31,23 @@ import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidationService; import jakarta.persistence.EntityManager; +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.*; 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; +import java.util.stream.Collectors; @Service public class TenantServiceImpl implements TenantService { @@ -57,6 +69,9 @@ public class TenantServiceImpl implements TenantService { private final XmlHandlingService xmlHandlingService; private final ErrorThesaurusProperties errors; private final ValidationService validationService; + private final EncryptionService encryptionService; + + private final TenantProperties properties; @Autowired public TenantServiceImpl( @@ -66,10 +81,10 @@ public class TenantServiceImpl implements TenantService { BuilderFactory builderFactory, ConventionService conventionService, MessageSource messageSource, QueryFactory queryFactory, - ResponseUtilsService responseUtilsService, + ResponseUtilsService responseUtilsService, XmlHandlingService xmlHandlingService, - ErrorThesaurusProperties errors, - ValidationService validationService) { + ErrorThesaurusProperties errors, + ValidationService validationService, EncryptionService encryptionService, TenantProperties properties) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -81,10 +96,12 @@ public class TenantServiceImpl implements TenantService { this.xmlHandlingService = xmlHandlingService; this.errors = errors; this.validationService = validationService; + this.encryptionService = encryptionService; + this.properties = properties; } @Override - public Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { + public Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields)); this.authorizationService.authorizeForce(Permission.EditTenant); @@ -94,8 +111,10 @@ public class TenantServiceImpl implements TenantService { TenantEntity data; if (isUpdate) { data = this.entityManager.find(TenantEntity.class, model.getId()); - if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tenant.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 == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) + throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); } else { data = new TenantEntity(); data.setId(UUID.randomUUID()); @@ -107,15 +126,88 @@ public class TenantServiceImpl implements TenantService { data.setName(model.getName()); data.setDescription(model.getDescription()); data.setUpdatedAt(Instant.now()); - data.setConfig(model.getConfig()); - + data.setConfig(this.xmlHandlingService.toXmlSafe(this.buildConfigEntity(model.getConfig()))); + if (isUpdate) this.entityManager.merge(data); - else this.entityManager.persist(data); + else this.entityManager.persist(data); this.entityManager.flush(); return this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Tenant._id), data); } + + private @NotNull TenantConfigEntity buildConfigEntity(TenantConfigPersist persist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + TenantConfigEntity data = new TenantConfigEntity(); + if (persist == null) return data; + if (persist.getDeposit() != null) { + data.setDeposit(this.buildDepositConfigEntity(persist.getDeposit())); + } + if (persist.getFileTransformers() != null) { + data.setFileTransformers(this.buildFileConfigEntity(persist.getFileTransformers())); + } + + return data; + } + + private @NotNull TenantDepositConfigEntity buildDepositConfigEntity(TenantDepositConfigPersist persist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + TenantDepositConfigEntity data = new TenantDepositConfigEntity(); + if (persist == null) return data; + + if (!this.conventionService.isListNullOrEmpty(persist.getSources())) { + data.setSources(new ArrayList<>()); + for (TenantSourcePersist sourcePersist : persist.getSources()) { + data.getSources().add(this.buildSourceEntity(sourcePersist)); + } + } + + return data; + } + + private @NotNull TenantFileTransformersConfigEntity buildFileConfigEntity(TenantFileTransformersConfigPersist persist) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + TenantFileTransformersConfigEntity data = new TenantFileTransformersConfigEntity(); + if (persist == null) return data; + + if (!this.conventionService.isListNullOrEmpty(persist.getSources())) { + data.setSources(new ArrayList<>()); + for (TenantSourcePersist sourcePersist : persist.getSources()) { + data.getSources().add(this.buildSourceEntity(sourcePersist)); + } + } + + return data; + } + + private @NotNull TenantSourceEntity buildSourceEntity(TenantSourcePersist persist) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + TenantSourceEntity data = new TenantSourceEntity(); + if (persist == null) return data; + + data.setUrl(persist.getUrl()); + data.setCodes(persist.getCodes()); + data.setIssuerUrl(persist.getIssuerUrl()); + data.setClientId(persist.getClientId()); + data.setClientSecret(this.encryptionService.encryptAES(persist.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv())); + //data.setClientSecret(persist.getClientSecret()); + + data.setScope(persist.getScope()); + + return data; + } + + @Override + public Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + if (model.getConfig() != null && model.getConfig().getDeposit() != null && model.getConfig().getDeposit().getSources() != null) { + for (TenantSource source : model.getConfig().getDeposit().getSources().stream().collect(Collectors.toList())) { + source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv())); + } + } + if (model.getConfig() != null && model.getConfig().getFileTransformers() != null && model.getConfig().getFileTransformers().getSources() != null) { + for (TenantSource source : model.getConfig().getFileTransformers().getSources().stream().collect(Collectors.toList())) { + source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv())); + } + } + return model; + } + @Override public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting : {}", id); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/TenantController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/TenantController.java index af16dfb60..78c5e481b 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/TenantController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/TenantController.java @@ -9,6 +9,7 @@ import eu.eudat.model.builder.TenantBuilder; import eu.eudat.model.censorship.TenantCensor; import eu.eudat.model.persist.TenantPersist; import eu.eudat.model.result.QueryResult; +import eu.eudat.model.tenantconfig.TenantSource; import eu.eudat.query.TenantQuery; import eu.eudat.query.lookup.TenantLookup; import eu.eudat.service.tenant.TenantService; @@ -30,13 +31,20 @@ import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.bind.annotation.*; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.management.InvalidApplicationException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @RestController @RequestMapping(path = "api/tenant") @@ -72,7 +80,7 @@ public class TenantController { } @PostMapping("query") - public QueryResult query(@RequestBody TenantLookup lookup) throws MyApplicationException, MyForbiddenException { + public QueryResult query(@RequestBody TenantLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug("querying {}", Tenant.class.getSimpleName()); this.censorFactory.censor(TenantCensor.class).censor(lookup.getProject(), null); @@ -80,6 +88,9 @@ public class TenantController { List data = query.collectAs(lookup.getProject()); List models = this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(lookup.getProject(), data); + for (Tenant model: models) { + models.set(models.indexOf(model), this.tenantService.decryptTenant(model)); + } long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size(); this.auditService.track(AuditableAction.Tenant_Query, "lookup", lookup); @@ -88,7 +99,7 @@ public class TenantController { } @GetMapping("{id}") - public Tenant get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + public Tenant get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("retrieving" + Tenant.class.getSimpleName()).And("id", id).And("fields", fieldSet)); this.censorFactory.censor(TenantCensor.class).censor(fieldSet, null); @@ -98,6 +109,8 @@ public class TenantController { if (model == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); + model = this.tenantService.decryptTenant(model); + this.auditService.track(AuditableAction.Tenant_Lookup, Map.ofEntries( new AbstractMap.SimpleEntry("id", id), new AbstractMap.SimpleEntry("fields", fieldSet) @@ -108,7 +121,7 @@ public class TenantController { @PostMapping("persist") @Transactional - public Tenant persist(@MyValidate @RequestBody TenantPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException { + public Tenant persist(@MyValidate @RequestBody TenantPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("persisting" + Tenant.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet)); this.censorFactory.censor(TenantCensor.class).censor(fieldSet, null); diff --git a/dmp-backend/web/src/main/resources/config/application.yml b/dmp-backend/web/src/main/resources/config/application.yml index 39eab1c42..6f847d3b8 100644 --- a/dmp-backend/web/src/main/resources/config/application.yml +++ b/dmp-backend/web/src/main/resources/config/application.yml @@ -19,4 +19,6 @@ spring: optional:classpath:config/swagger.yml[.yml], optional:classpath:config/swagger-${spring.profiles.active}.yml[.yml], optional:file:../config/swagger-${spring.profiles.active}.yml[.yml], optional:classpath:config/deposit.yml[.yml], optional:classpath:config/deposit-${spring.profiles.active}.yml[.yml], optional:file:../config/deposit-${spring.profiles.active}.yml[.yml], optional:classpath:config/errors.yml[.yml], optional:classpath:config/errors-${spring.profiles.active}.yml[.yml], optional:file:../config/errors-${spring.profiles.active}.yml[.yml], - optional:classpath:config/reference-type.yml[.yml], optional:classpath:config/reference-type-${spring.profiles.active}.yml[.yml], optional:file:../config/reference-type-${spring.profiles.active}.yml[.yml] + optional:classpath:config/reference-type.yml[.yml], optional:classpath:config/reference-type-${spring.profiles.active}.yml[.yml], optional:file:../config/reference-type-${spring.profiles.active}.yml[.yml], + optional:classpath:config/tenant.yml[.yml], optional:classpath:config/tenant-${spring.profiles.active}.yml[.yml], optional:file:../config/tenant-${spring.profiles.active}.yml[.yml] + diff --git a/dmp-backend/web/src/main/resources/config/tenant.yml b/dmp-backend/web/src/main/resources/config/tenant.yml new file mode 100644 index 000000000..2b442bb71 --- /dev/null +++ b/dmp-backend/web/src/main/resources/config/tenant.yml @@ -0,0 +1,3 @@ +tenant: + configEncryptionAesKey: 42J7rLaej8X+kUGR + configEncryptionAesIv: oL859DQRZP+AhfQ+ \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/tenant/tenant.ts b/dmp-frontend/src/app/core/model/tenant/tenant.ts index 6a655b5bf..114e95fc8 100644 --- a/dmp-frontend/src/app/core/model/tenant/tenant.ts +++ b/dmp-frontend/src/app/core/model/tenant/tenant.ts @@ -4,7 +4,33 @@ export interface Tenant extends BaseEntity{ name: string; code: string; description: string; - config: string; + config?: TenantConfig; +} + +export interface TenantConfig{ + deposit: TenantDepositConfig; + fileTransformers: TenantFileTransformersConfig; +} + +export interface TenantDepositConfig{ + sources: TenantSource[]; +} + +export interface TenantFileTransformersConfig{ + sources: TenantSource[]; +} + +export interface TenantSource{ + url: string; + codes: string[]; + issuerUrl: string; + clientId: string; + clientSecret: string; + scope: string; +} + +export interface SourceCode{ + code: string; } //persist @@ -13,5 +39,31 @@ export interface TenantPersist extends BaseEntityPersist{ name: string; code: string; description: string; - config: string; + config?: TenantConfigPersist; +} + +export interface TenantConfigPersist{ + deposit: TenantDepositConfigPersist; + fileTransformers: TenantFileTransformersConfigPersist; +} + +export interface TenantDepositConfigPersist{ + sources: TenantSourcePersist[]; +} + +export interface TenantFileTransformersConfigPersist{ + sources: TenantSourcePersist[]; +} + +export interface TenantSourcePersist{ + url: string; + codes: string[]; + issuerUrl: string; + clientId: string; + clientSecret: string; + scope: string; +} + +export interface SourceCodePersist{ + code: string; } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html index 6726eb2ed..48e3f4bcd 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html @@ -5,7 +5,7 @@
- +
+ +
+
+
+
+ {{'TENANT-EDITOR.FIELDS.SOURCE' | translate}} {{sourceIndex + 1}} +
+
+ +
+
+
+
+ + {{'TENANT-EDITOR.FIELDS.URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.SCOPE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CODES' | translate}} + + + {{code}} + + + + + +
+
+
+
+
+
+

+ {{'TENANT-EDITOR.FIELDS.FILE-TRANSFORMERS' | translate}} + +

+
+
+
+
+ {{'TENANT-EDITOR.FIELDS.SOURCE' | translate}} {{sourceIndex + 1}} +
+
+ +
+
+
+
+ + {{'TENANT-EDITOR.FIELDS.URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.SCOPE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'TENANT-EDITOR.FIELDS.CODES' | translate}} + + + {{code}} + + + + + +
+
+
+
diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.scss b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.scss index 960ac2616..66f9e4fa8 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.scss @@ -23,21 +23,6 @@ background-color: #b0b0b0; } -.finalize-btn { - border-radius: 30px; - border: 1px solid var(--primary-color); - background: transparent; - padding-left: 2em; - padding-right: 2em; - box-shadow: 0px 3px 6px #1E202029; - color: var(--primary-color); - &:disabled{ - background-color: #CBCBCB; - color: #FFF; - border: 0px; - } -} - .action-btn { border-radius: 30px; background-color: var(--secondary-color); @@ -55,62 +40,4 @@ color: #FFF; border: 0px; } -} - -.dlt-section-btn { - margin: 0; - position: absolute; - top: 50%; - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} - -.section-input { - position: relative; -} - -.section-input .arrows { - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); -} - -.action-list-item{ - display: flex; - align-items: center; - cursor: pointer; - - .action-list-icon{ - font-size: 1.2em; - // padding-right: 1em; - // width: 14px; - // margin-right: 0.5em; - // margin-left: -.09em; - // height: auto; - color: var(--primary-color); - } - - .action-list-text{ - font-size: 1em; - color: var(--primary-color); - } -} - -.field-delete{ - align-items: center; - display: flex; - cursor: pointer; - - .field-delete-icon{ - font-size: 1.2em; - width: 14px; - color: var(--primary-color); - } - - .field-delete-text{ - font-size: 1em; - margin-left: 0.5em; - color: var(--primary-color); - } } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts index 4ce5112ee..a2e83aadf 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts @@ -10,10 +10,8 @@ import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { DatePipe } from '@angular/common'; import { IsActive } from '@app/core/common/enum/is-active.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum'; -import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; import { Tenant, TenantPersist } from '@app/core/model/tenant/tenant'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { DmpService } from '@app/core/services/dmp/dmp.service'; import { LoggingService } from '@app/core/services/logging/logging-service'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { FileUtils } from '@app/core/services/utilities/file-utils.service'; @@ -29,7 +27,8 @@ import { TranslateService } from '@ngx-translate/core'; import { map, takeUntil } from 'rxjs/operators'; import { TenantEditorResolver } from './tenant-editor.resolver'; import { TenantEditorService } from './tenant-editor.service'; -import { TenantEditorModel } from './tenant-editor.model'; +import { TenantEditorModel, TenantSourceEditorModel } from './tenant-editor.model'; +import { MatChipEditedEvent, MatChipInputEvent } from '@angular/material/chips'; @Component({ @@ -44,6 +43,8 @@ export class TenantEditorComponent extends BaseEditor isDeleted = false; formGroup: UntypedFormGroup = null; showInactiveDetails = false; + depositCodes: string[] = []; + fileTransformersCodes: string[] = []; protected get canDelete(): boolean { return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteTenant); @@ -103,6 +104,11 @@ export class TenantEditorComponent extends BaseEditor prepareForm(data: Tenant) { try { this.editorModel = data ? new TenantEditorModel().fromModel(data) : new TenantEditorModel(); + + if(data && data.config){ + if(data.config.deposit && data.config.deposit.sources) data.config.deposit.sources.forEach(source => this.depositCodes = source.codes) + if(data.config.fileTransformers && data.config.fileTransformers.sources) data.config.fileTransformers.sources.forEach(source => this.fileTransformersCodes = source.codes) + } this.isDeleted = data ? data.isActive === IsActive.Inactive : false; this.buildForm(); } catch (error) { @@ -182,4 +188,85 @@ export class TenantEditorComponent extends BaseEditor this.formService.validateAllFormFields(this.formGroup); } + // + // deposit source + // + addDepositSource(): void { + const source: TenantSourceEditorModel = new TenantSourceEditorModel(); + (this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(source.buildForm()); + } + + removeDepositSource(sourceIndex: number): void { + (this.formGroup.get('config').get('deposit').get('sources') as FormArray).removeAt(sourceIndex); + } + + // deposit source codes + + addDepositCode(event: MatChipInputEvent): void { + const value = (event.value || '').trim(); + + if (value) this.depositCodes.push(value) + event.chipInput!.clear(); + } + + removeDepositCode(code: string): void { + const index = this.depositCodes.indexOf(code); + + if (index >= 0) this.depositCodes.splice(index, 1); + } + + editDepositCode(code: string, event: MatChipEditedEvent) { + const value = event.value.trim(); + + // Remove code if it no longer has a value + if (!value) { + this.removeDepositCode(code); + return; + } + + const index = this.depositCodes.indexOf(code); + if (index >= 0) this.depositCodes[index] = value + } + + // + // fileTransformers source + // + addFileSource(): void { + const source: TenantSourceEditorModel = new TenantSourceEditorModel(); + (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(source.buildForm()); + } + + removeFileSource(sourceIndex: number): void { + (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).removeAt(sourceIndex); + } + + // fileTransformers source codes + + addFileCode(event: MatChipInputEvent): void { + const value = (event.value || '').trim(); + + if (value) this.fileTransformersCodes.push(value) + event.chipInput!.clear(); + } + + removeFileCode(code: string): void { + const index = this.fileTransformersCodes.indexOf(code); + + if (index >= 0) this.fileTransformersCodes.splice(index, 1); + } + + editFileCode(code: string, event: MatChipEditedEvent) { + const value = event.value.trim(); + + // Remove code if it no longer has a value + if (!value) { + this.removeFileCode(code); + return; + } + + const index = this.fileTransformersCodes.indexOf(code); + if (index >= 0) this.fileTransformersCodes[index] = value + } + + } diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts index d8e667b8b..268d4554d 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts @@ -1,5 +1,5 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; -import { Tenant, TenantPersist } from "@app/core/model/tenant/tenant"; +import { Tenant, TenantConfig, TenantConfigPersist, TenantDepositConfig, TenantDepositConfigPersist, TenantFileTransformersConfig, TenantFileTransformersConfigPersist, TenantPersist, TenantSource, TenantSourcePersist } from "@app/core/model/tenant/tenant"; import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; @@ -9,7 +9,7 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist name: string; code: string; description: string; - config: string; + config: TenantConfigEditorModel = new TenantConfigEditorModel(); permissions: string[]; public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); @@ -23,7 +23,7 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist this.name = item.name; this.code = item.code; this.description = item.description; - this.config = item.config; + if (item.config) this.config = new TenantConfigEditorModel().fromModel(item.config); } return this; } @@ -36,7 +36,9 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators], code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators], - config: [{ value: this.config, disabled: disabled }, context.getValidation('config').validators], + config: this.config.buildForm({ + rootPath: `config.` + }), hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] }); } @@ -48,7 +50,6 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'name')] }); baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'code')] }); baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); - baseValidationArray.push({ key: 'config', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'config')] }); baseValidationArray.push({ key: 'hash', validators: [] }); baseContext.validation = baseValidationArray; @@ -56,3 +57,241 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist } } +export class TenantConfigEditorModel implements TenantConfigPersist { + deposit: TenantDepositConfigEditorModel = new TenantDepositConfigEditorModel(); + fileTransformers: TenantFileTransformersConfigEditorModel = new TenantFileTransformersConfigEditorModel(); + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: TenantConfig): TenantConfigEditorModel { + if (item) { + if (item.deposit) this.deposit = new TenantDepositConfigEditorModel().fromModel(item.deposit); + if (item.fileTransformers) this.fileTransformers = new TenantFileTransformersConfigEditorModel().fromModel(item.fileTransformers); + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = TenantConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + deposit: this.deposit.buildForm({ + rootPath: `deposit.` + }), + fileTransformers: this.fileTransformers.buildForm({ + rootPath: `fileTransformers.` + }), + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class TenantDepositConfigEditorModel implements TenantDepositConfigPersist { + sources: TenantSourceEditorModel[]= []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: TenantDepositConfig): TenantDepositConfigEditorModel { + if (item) { + if (item.sources) { item.sources.map(x => this.sources.push(new TenantSourceEditorModel().fromModel(x))); } + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = TenantDepositConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + sources: this.formBuilder.array( + (this.sources ?? []).map( + (item, index) => new TenantSourceEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `sources[${index}].` + }), context.getValidation('sources') + ) + ), + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'sources', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}sources`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class TenantFileTransformersConfigEditorModel implements TenantFileTransformersConfigPersist { + sources: TenantSourceEditorModel[]= []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: TenantFileTransformersConfig): TenantFileTransformersConfigEditorModel { + if (item) { + if (item.sources) { item.sources.map(x => this.sources.push(new TenantSourceEditorModel().fromModel(x))); } + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = TenantFileTransformersConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + sources: this.formBuilder.array( + (this.sources ?? []).map( + (item, index) => new TenantSourceEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `sources[${index}].` + }), context.getValidation('sources') + ) + ), + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'sources', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}sources`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class TenantSourceEditorModel implements TenantSourcePersist { + url: string; + codes: string[]= []; + issuerUrl: string; + clientId: string; + clientSecret: string; + scope: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: TenantSource): TenantSourceEditorModel { + if (item) { + this.url = item.url; + //if (item.codes) { item.codes.map(x => this.codes.push(new SourceCodeEditorModel().fromModel(x))); } + this.codes = item.codes; + this.issuerUrl = item.issuerUrl; + this.clientId = item.clientId; + this.clientSecret = item.clientSecret; + this.scope = item.scope; + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = TenantSourceEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators], + issuerUrl: [{ value: this.issuerUrl, disabled: disabled }, context.getValidation('issuerUrl').validators], + clientId: [{ value: this.clientId, disabled: disabled }, context.getValidation('clientId').validators], + clientSecret: [{ value: this.clientSecret, disabled: disabled }, context.getValidation('clientSecret').validators], + scope: [{ value: this.scope, disabled: disabled }, context.getValidation('scope').validators], + codes: [{ value: this.codes, disabled: disabled }, context.getValidation('codes').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'url', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}url`)] }); + baseValidationArray.push({ key: 'codes', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}codes`)] }); + baseValidationArray.push({ key: 'issuerUrl', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}issuerUrl`)] }); + baseValidationArray.push({ key: 'clientId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientId`)] }); + baseValidationArray.push({ key: 'clientSecret', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientSecret`)] }); + baseValidationArray.push({ key: 'scope', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}scope`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.resolver.ts index 0eb0d764e..f6d083a27 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.resolver.ts +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.resolver.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Tenant } from '@app/core/model/tenant/tenant'; +import { SourceCode, Tenant, TenantConfig, TenantDepositConfig, TenantSource } from '@app/core/model/tenant/tenant'; import { TenantService } from '@app/core/services/tenant/tenant.service'; import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; import { BaseEditorResolver } from '@common/base/base-editor.resolver'; @@ -22,7 +22,22 @@ export class TenantEditorResolver extends BaseEditorResolver { nameof(x => x.name), nameof(x => x.code), nameof(x => x.description), - nameof(x => x.config), + + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.url)].join('.'), + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.issuerUrl)].join('.'), + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.clientId)].join('.'), + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.clientSecret)].join('.'), + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.scope)].join('.'), + [nameof(x => x.config), nameof(x => x.deposit), nameof(x => x.sources), nameof(x => x.codes)].join('.'), + + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.url)].join('.'), + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.issuerUrl)].join('.'), + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.clientId)].join('.'), + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.clientSecret)].join('.'), + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.scope)].join('.'), + [nameof(x => x.config), nameof(x => x.fileTransformers), nameof(x => x.sources), nameof(x => x.codes)].join('.'), + + nameof(x => x.createdAt), nameof(x => x.updatedAt), nameof(x => x.hash), diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 9ec441a51..58f4954dd 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1205,12 +1205,27 @@ "FIELDS": { "NAME": "Name", "CODE": "Code", - "DESCRIPTION": "Description" + "DESCRIPTION": "Description", + "DESCRIPTION-PLACEHOLDER": "Tenant description", + "DEPOSIT":"Deposit", + "SOURCE": "Source", + "URL":"Url", + "CODES": "Codes", + "CODES-PLACEHOLDER": "New code ..", + "ISSUER-URL": "Issuer Url", + "CLIENT-ID": "Client ID", + "CLIENT-SECRET": "Client Secret", + "SCOPE": "Scope", + "FILE-TRANSFORMERS":"File transformers" }, "ACTIONS": { "SAVE": "Save", "CANCEL": "Cancel", - "DELETE": "Delete" + "DELETE": "Delete", + "ADD-SOURCE": "Add Source", + "REMOVE-SOURCE": "Remove Source", + "ADD-CODE": "Add Code", + "REMOVE-CODE": "Remove Code" }, "CONFIRM-DELETE-DIALOG": { "MESSAGE": "Would you like to delete this Tenant?", diff --git a/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpDatasetProfileMigrationService.java b/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpDatasetProfileMigrationService.java index 63ad661f1..3ccb551c4 100644 --- a/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpDatasetProfileMigrationService.java +++ b/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpDatasetProfileMigrationService.java @@ -1,33 +1,25 @@ package eu.old.eudat.migration; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.XmlHandlingService; import eu.eudat.commons.enums.IsActive; -import eu.eudat.commons.enums.ReferenceFieldDataType; -import eu.eudat.commons.enums.ReferenceSourceType; -import eu.eudat.commons.enums.ReferenceType; -import eu.eudat.commons.types.reference.FieldEntity; -import eu.eudat.data.DescriptionTemplateEntity; import eu.eudat.data.DmpBlueprintEntity; import eu.eudat.data.DmpDescriptionTemplateEntity; -import eu.eudat.data.ReferenceEntity; -import eu.eudat.model.DmpBlueprint; -import eu.eudat.query.DescriptionTemplateQuery; import eu.eudat.query.DmpBlueprintQuery; import eu.eudat.query.DmpDescriptionTemplateQuery; import eu.old.eudat.data.dao.entities.DmpDatasetProfileDao; -import eu.old.eudat.data.dao.entities.ResearcherDao; import eu.old.eudat.data.entities.DMPDatasetProfile; -import eu.old.eudat.data.entities.Researcher; import eu.old.eudat.logic.services.operations.DatabaseRepository; -import eu.old.eudat.queryable.QueryableList; -import gr.cite.tools.data.query.Paging; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.logging.LoggerService; import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; import jakarta.xml.bind.JAXBException; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -36,6 +28,7 @@ import eu.eudat.commons.types.dmpblueprint.DefinitionEntity; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; +import java.security.SecureRandom; import java.time.Instant; import java.util.*; @@ -127,34 +120,36 @@ public class DmpDatasetProfileMigrationService { private void removeDuplicates() { logger.debug("Checking for duplicates on DmpDescriptionTemplate table after migration"); - int page = 0; DmpDescriptionTemplateQuery dmpDescriptionTemplateQuery = this.queryFactory.query(DmpDescriptionTemplateQuery.class); - List dmpDescriptionTemplateEntities; - do { - dmpDescriptionTemplateQuery.setPage(new Paging(page * PageSize, PageSize)); - dmpDescriptionTemplateEntities = dmpDescriptionTemplateQuery.collect(); + long total = dmpDescriptionTemplateQuery.count(); + logger.debug("Record count to check: {}", total); - DmpDescriptionTemplateEntity previousEntity = null; - for(DmpDescriptionTemplateEntity currentEntity : dmpDescriptionTemplateEntities) { - if (previousEntity == null) { - previousEntity = currentEntity; - continue; - } - if (currentEntity.getDmpId().equals(previousEntity.getDmpId()) - && currentEntity.getDescriptionTemplateGroupId().equals(previousEntity.getDescriptionTemplateGroupId()) - && currentEntity.getSectionId().equals(previousEntity.getSectionId()) - ) { - logger.warn("Removing found duplicate DmpDescriptionTemplate entity ({}) after DmpDatasetProfiles migration", currentEntity.getId()); - this.entityManager.remove(currentEntity); - } else { - previousEntity = currentEntity; - } - } - entityManager.flush(); + CriteriaBuilder b = this.entityManager.getCriteriaBuilder(); + CriteriaQuery criteria = b.createQuery(Tuple.class); + Root root = criteria.from(DmpDescriptionTemplateEntity.class); + criteria.groupBy(Arrays.asList(root.get("dmp"), root.get("descriptionTemplateGroupId"), root.get("sectionId"))); + criteria.multiselect(root.get("dmp"), root.get("descriptionTemplateGroupId"), root.get("sectionId"), b.count(root)); + List resultList = this.entityManager.createQuery(criteria).getResultList(); - page++; - } while (!dmpDescriptionTemplateEntities.isEmpty() && !TestMode); + List duplicatesList = resultList.stream().filter(x -> (long) x.get(3) > 1).toList(); + CriteriaDelete delete = b.createCriteriaDelete(DmpDescriptionTemplateEntity.class); + Root root1 = delete.from(DmpDescriptionTemplateEntity.class); + for (Tuple duplicate : duplicatesList) { + List duplicateEntities = dmpDescriptionTemplateQuery + .dmpIds((UUID) duplicate.get(0)) + .descriptionTemplateGroupIds((UUID) duplicate.get(1)) + .sectionIds((UUID) duplicate.get(2)) + .collect(); + + List toDelete = new ArrayList<>(duplicateEntities.stream().map(DmpDescriptionTemplateEntity::getId).toList()); + int random = new SecureRandom().nextInt(0, toDelete.size()); + toDelete.remove(random); + delete.where(root1.get("id").in(toDelete)); + this.entityManager.createQuery(delete).executeUpdate(); + } + + entityManager.flush(); } @JsonIgnoreProperties({"validationErrorModel"}) diff --git a/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpUserMigrationService.java b/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpUserMigrationService.java new file mode 100644 index 000000000..fb0ebaa33 --- /dev/null +++ b/dmp-migration-tool/web/src/main/java/eu/old/eudat/migration/DmpUserMigrationService.java @@ -0,0 +1,66 @@ +package eu.old.eudat.migration; + +import eu.eudat.commons.enums.DmpUserRole; +import eu.eudat.commons.enums.IsActive; +import eu.eudat.data.DmpUserEntity; +import eu.old.eudat.data.dao.entities.UserDmpDao; +import eu.old.eudat.data.entities.UserDMP; +import eu.old.eudat.logic.services.operations.DatabaseRepository; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; + +@Service +public class DmpUserMigrationService { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DmpUserMigrationService.class)); + + private final DatabaseRepository databaseRepository; + + private final EntityManager entityManager; + + private static final int PageSize = 500; + + private static final boolean TestMode = false; + + public DmpUserMigrationService(DatabaseRepository databaseRepository, EntityManager entityManager) { + this.databaseRepository = databaseRepository; + this.entityManager = entityManager; + } + + public void migrate() { + UserDmpDao userDmpDao = databaseRepository.getUserDmpDao(); + long total = userDmpDao.asQueryable().count(); + logger.debug("Migrate UserDMP Total : " + total); + int page = 0; + + List items; + do { + items = userDmpDao.asQueryable().orderBy((builder, root) -> builder.asc(root.get("ID"))).skip(page * PageSize).take(PageSize).toList(); + if (items != null && !items.isEmpty()) { + logger.debug("Migrate UserDMP " + page * PageSize + " of " + total); + for (UserDMP item : items) { + entityManager.detach(item); + + DmpUserEntity data = new DmpUserEntity(); + data.setId(item.getId()); + data.setDmp(item.getDmp().getId()); + data.setUserId(item.getUser().getId()); + data.setRole(DmpUserRole.of(item.getRole().shortValue())); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + this.entityManager.persist(data); + } + this.entityManager.flush(); + + page++; + } + } while (items != null && !items.isEmpty() && !TestMode); + } + +} diff --git a/dmp-migration-tool/web/src/main/java/eu/old/eudat/publicapi/migration/MigrationController.java b/dmp-migration-tool/web/src/main/java/eu/old/eudat/publicapi/migration/MigrationController.java index baa049994..c81f3523c 100644 --- a/dmp-migration-tool/web/src/main/java/eu/old/eudat/publicapi/migration/MigrationController.java +++ b/dmp-migration-tool/web/src/main/java/eu/old/eudat/publicapi/migration/MigrationController.java @@ -34,6 +34,7 @@ public class MigrationController { private final OrganizationMigrationService organizationMigrationService; private final ReferenceMigrationService referenceMigrationService; private final UserContactInfoMigrationService userContactInfoMigrationService; + private final DmpUserMigrationService dmpUserMigrationService; public MigrationController( DmpMigrationService dmpMigrationService, @@ -49,7 +50,8 @@ public class MigrationController { ServiceMigrationService serviceMigrationService, OrganizationMigrationService organizationMigrationService, ReferenceMigrationService referenceMigrationService, - UserContactInfoMigrationService userContactInfoMigrationService) { + UserContactInfoMigrationService userContactInfoMigrationService, + DmpUserMigrationService dmpUserMigrationService) { this.dmpMigrationService = dmpMigrationService; this.datasetMigrationService = datasetMigrationService; this.dmpDatasetProfileMigrationService = dmpDatasetProfileMigrationService; @@ -64,6 +66,7 @@ public class MigrationController { this.organizationMigrationService = organizationMigrationService; this.referenceMigrationService = referenceMigrationService; this.userContactInfoMigrationService = userContactInfoMigrationService; + this.dmpUserMigrationService = dmpUserMigrationService; } @GetMapping("all") @@ -94,7 +97,9 @@ public class MigrationController { public boolean migrateContacts() { this.userContactInfoMigrationService.migrate(); return true; + } + @GetMapping("dmps") @Transactional @@ -139,6 +144,13 @@ public class MigrationController { return true; } + @GetMapping("dmp-users") + @Transactional + public boolean migrateDmpUsers() { + this.dmpUserMigrationService.migrate(); + return true; + } + @GetMapping("dataset-references") @Transactional public boolean migrateDatasetReferences() {