From 3990be182c4b90a00185d61600a4d3f23edb2289 Mon Sep 17 00:00:00 2001 From: amentis Date: Thu, 14 Dec 2023 18:19:43 +0200 Subject: [PATCH] add notifications to user,description,description-template --- .../description/DescriptionServiceImpl.java | 82 +++++++-- .../DescriptionTemplateServiceImpl.java | 14 +- .../eu/eudat/service/dmp/DmpServiceImpl.java | 2 +- .../eu/eudat/service/user/UserService.java | 10 ++ .../eudat/service/user/UserServiceImpl.java | 158 ++++++++++++++++-- .../main/resources/config/notification.yml | 4 +- .../target/classes/config/notification.yml | 4 +- .../eudat/controllers/v2/UserController.java | 59 +++++++ .../managers/DataManagementPlanManager.java | 14 +- .../ConfirmationEmailServiceImpl.java | 37 +--- .../utilities/InvitationServiceImpl.java | 5 +- .../00.01.040_addActionConfirmation.sql | 35 ++++ 12 files changed, 347 insertions(+), 77 deletions(-) create mode 100644 dmp-db-scema/updates/00.01.040_addActionConfirmation.sql diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java index 655eee0b3..47d3343fc 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java @@ -5,20 +5,24 @@ import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.XmlHandlingService; +import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.commons.enums.DescriptionStatus; import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.enums.notification.NotificationContactType; import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.types.description.FieldEntity; import eu.eudat.commons.types.description.PropertyDefinitionEntity; -import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity; -import eu.eudat.commons.types.descriptiontemplate.SectionEntity; +import eu.eudat.commons.types.notification.*; import eu.eudat.commons.types.reference.DefinitionEntity; +import eu.eudat.configurations.notification.NotificationProperties; import eu.eudat.convention.ConventionService; import eu.eudat.data.*; import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.event.DescriptionTouchedEvent; import eu.eudat.event.EventBroker; +import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEvent; +import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEventHandler; import eu.eudat.model.*; import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.deleter.DescriptionDeleter; @@ -35,6 +39,7 @@ import eu.eudat.query.*; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; @@ -86,6 +91,11 @@ public class DescriptionServiceImpl implements DescriptionService { private final UserScope userScope; private final XmlHandlingService xmlHandlingService; + private final NotificationIntegrationEventHandler eventHandler; + + private final NotificationProperties notificationProperties; + + @Autowired public DescriptionServiceImpl( EntityManager entityManager, @@ -97,9 +107,9 @@ public class DescriptionServiceImpl implements DescriptionService { MessageSource messageSource, EventBroker eventBroker, QueryFactory queryFactory, - JsonHandlingService jsonHandlingService, - UserScope userScope, - XmlHandlingService xmlHandlingService) { + JsonHandlingService jsonHandlingService, + UserScope userScope, + XmlHandlingService xmlHandlingService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -112,6 +122,8 @@ public class DescriptionServiceImpl implements DescriptionService { this.jsonHandlingService = jsonHandlingService; this.userScope = userScope; this.xmlHandlingService = xmlHandlingService; + this.eventHandler = eventHandler; + this.notificationProperties = notificationProperties; } //region Persist @@ -169,13 +181,7 @@ public class DescriptionServiceImpl implements DescriptionService { this.entityManager.flush(); if (isUpdate){ - if (!data.getStatus() .equals(DescriptionStatus.Finalized)) { - //TODO - //this.sendNotification(dataset1, dataset1.getDmp(), userInfo, NotificationType.DATASET_MODIFIED); - } else { - //TODO - //this.sendNotification(dataset1, dataset1.getDmp(), userInfo, NotificationType.DATASET_MODIFIED_FINALISED); - } + this.sendNotification(data); } //this.deleteOldFilesAndAddNew(datasetWizardModel, userInfo); //TODO @@ -183,6 +189,58 @@ public class DescriptionServiceImpl implements DescriptionService { return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Description._id), data); } + private void sendNotification(DescriptionEntity description) throws InvalidApplicationException { + List existingUsers = this.queryFactory.query(DmpUserQuery.class) + .dmpIds(description.getDmpId()) + .isActives(IsActive.Active) + .collect(); + + if (existingUsers == null || existingUsers.size() <= 1){ + return; + } + for (DmpUserEntity dmpUser : existingUsers) { + if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){ + UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first(); + if (user != null){ + NotificationIntegrationEvent event = new NotificationIntegrationEvent(); + event.setUserId(this.userScope.getUserId()); + + UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId()); + query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal)); + + List contactPairs = new ArrayList<>(); + contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue())); + NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); + event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); + event.setContactTypeHint(NotificationContactType.EMAIL); + + event = this.applyNotificationType(description.getStatus(), event); + NotificationFieldData data = new NotificationFieldData(); + List fieldInfoList = new ArrayList<>(); + fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName())); + fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserId()).first().getName())); + fieldInfoList.add(new FieldInfo("{name}", DataType.String, description.getLabel())); + fieldInfoList.add(new FieldInfo("{id}", DataType.String, description.getId().toString())); + data.setFields(fieldInfoList); + event.setData(jsonHandlingService.toJsonSafe(data)); + + eventHandler.handle(event); + } + } + } + } + + private NotificationIntegrationEvent applyNotificationType(DescriptionStatus status, NotificationIntegrationEvent event) throws InvalidApplicationException { + switch (status) { + case Draft: + event.setNotificationType(UUID.fromString(notificationProperties.getDescriptionModified())); + case Finalized: + event.setNotificationType(UUID.fromString(notificationProperties.getDescriptionFinalised())); + default: + throw new InvalidApplicationException("Unsupported Description Status."); + } + } + // public List getFieldById(String id){ // List fieldEntities = new ArrayList<>(); // if (id == null || id.isBlank()) return fieldEntities; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java index 3d0a88588..2152b5a8b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java @@ -63,7 +63,6 @@ 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.core.env.Environment; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.xml.sax.SAXException; @@ -102,7 +101,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic private final ErrorThesaurusProperties errors; private final ValidationService validationService; private final TenantScope tenantScope; - private final Environment environment; private final ResponseUtilsService responseUtilsService; private final StorageFileService storageFileService; private final JsonHandlingService jsonHandlingService; @@ -118,7 +116,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, - FieldDataHelperServiceProvider fieldDataHelperServiceProvider, QueryFactory queryFactory, ErrorThesaurusProperties errors, ValidationService validationService, TenantScope tenantScope, Environment environment, ResponseUtilsService responseUtilsService, StorageFileService storageFileService, JsonHandlingService jsonHandlingService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties) { + FieldDataHelperServiceProvider fieldDataHelperServiceProvider, QueryFactory queryFactory, ErrorThesaurusProperties errors, ValidationService validationService, TenantScope tenantScope, ResponseUtilsService responseUtilsService, StorageFileService storageFileService, JsonHandlingService jsonHandlingService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties) { this.entityManager = entityManager; this.userScope = userScope; this.authorizationService = authorizationService; @@ -132,7 +130,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic this.errors = errors; this.validationService = validationService; this.tenantScope = tenantScope; - this.environment = environment; this.responseUtilsService = responseUtilsService; this.storageFileService = storageFileService; this.jsonHandlingService = jsonHandlingService; @@ -209,13 +206,13 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class).delete(toDelete); } - private void sendJoinMail(UserDescriptionTemplateEntity userDatasetProfile) throws InvalidApplicationException { + private void sendJoinMail(UserDescriptionTemplateEntity userDescriptionTemplate) throws InvalidApplicationException { NotificationIntegrationEvent event = new NotificationIntegrationEvent(); event.setTenant(tenantScope.getTenant()); event.setUserId(userScope.getUserIdSafe()); - UserEntity user = this.entityManager.find(UserEntity.class, userDatasetProfile.getUserId()); - DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).isActive(IsActive.Active).ids(userDatasetProfile.getDescriptionTemplateId()).first(); + UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId()); + DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).isActive(IsActive.Active).ids(userDescriptionTemplate.getDescriptionTemplateId()).first(); UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId()); query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal)); @@ -225,12 +222,11 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); event.setContactTypeHint(NotificationContactType.EMAIL); - event.setNotificationType(UUID.fromString(notificationProperties.getTemplate())); + event.setNotificationType(UUID.fromString(notificationProperties.getDescriptionTemplateInvitation())); NotificationFieldData data = new NotificationFieldData(); List fieldInfoList = new ArrayList<>(); fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName())); fieldInfoList.add(new FieldInfo("{templateName}", DataType.String, descriptionTemplate.getLabel())); - fieldInfoList.add(new FieldInfo("{host}", DataType.String, this.environment.getProperty("dmp.domain"))); fieldInfoList.add(new FieldInfo("{templateID}", DataType.String, descriptionTemplate.getId().toString())); data.setFields(fieldInfoList); event.setData(jsonHandlingService.toJsonSafe(data)); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java index 6e9007431..64842335e 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java @@ -741,7 +741,7 @@ public class DmpServiceImpl implements DmpService { public void dmpInvitationAccept(String token) throws InvalidApplicationException { - ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).first(); + ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.DmpInvitation).isActive(IsActive.Active).first(); if (action == null){ throw new InvalidApplicationException("Token does not exist!"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java index d18df22c4..a3fd6e486 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java @@ -11,6 +11,7 @@ import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; +import jakarta.xml.bind.JAXBException; import javax.management.InvalidApplicationException; import java.io.IOException; @@ -28,4 +29,13 @@ public interface UserService { byte[] exportCsv() throws IOException; User patchRoles(UserRolePatchPersist model, FieldSet fields) throws InvalidApplicationException; + + void sendMergeAccountConfirmation(String email) throws InvalidApplicationException, JAXBException; + + void sendRemoveCredentialConfirmation(String email) throws InvalidApplicationException, JAXBException; + + void confirmMergeAccount(String token) throws InvalidApplicationException; + + void confirmRemoveCredential(String token) throws InvalidApplicationException; + } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java index 313eb356f..1fa74f1dd 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java @@ -5,28 +5,34 @@ import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.OwnedResource; import eu.eudat.authorization.Permission; import eu.eudat.commons.JsonHandlingService; +import eu.eudat.commons.XmlHandlingService; +import eu.eudat.commons.enums.ActionConfirmationStatus; +import eu.eudat.commons.enums.ActionConfirmationType; import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.enums.notification.NotificationContactType; import eu.eudat.commons.scope.user.UserScope; +import eu.eudat.commons.types.actionconfirmation.EmailConfirmationEntity; +import eu.eudat.commons.types.notification.*; import eu.eudat.commons.types.user.AdditionalInfoEntity; +import eu.eudat.configurations.notification.NotificationProperties; import eu.eudat.convention.ConventionService; -import eu.eudat.data.UserCredentialEntity; -import eu.eudat.data.UserEntity; -import eu.eudat.data.UserRoleEntity; +import eu.eudat.data.*; import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.event.UserTouchedEvent; import eu.eudat.event.EventBroker; +import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEvent; +import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEventHandler; import eu.eudat.model.User; import eu.eudat.model.UserContactInfo; import eu.eudat.model.builder.UserBuilder; +import eu.eudat.model.deleter.UserCredentialDeleter; import eu.eudat.model.deleter.UserDeleter; import eu.eudat.model.deleter.UserRoleDeleter; -import eu.eudat.model.persist.UserAdditionalInfoPersist; -import eu.eudat.model.persist.UserPersist; -import eu.eudat.model.persist.UserRolePatchPersist; -import eu.eudat.query.UserCredentialQuery; -import eu.eudat.query.UserQuery; -import eu.eudat.query.UserRoleQuery; +import eu.eudat.model.persist.*; +import eu.eudat.model.persist.actionconfirmation.EmailConfirmationPersist; +import eu.eudat.query.*; +import eu.eudat.service.actionconfirmation.ActionConfirmationService; import eu.eudat.service.keycloak.KeycloakRole; import eu.eudat.service.keycloak.KeycloakService; import gr.cite.commons.web.authz.service.AuthorizationService; @@ -42,6 +48,7 @@ import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import jakarta.persistence.EntityManager; +import jakarta.xml.bind.JAXBException; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.QuoteMode; @@ -82,10 +89,14 @@ public class UserServiceImpl implements UserService { private final MessageSource messageSource; private final EventBroker eventBroker; private final JsonHandlingService jsonHandlingService; + private final XmlHandlingService xmlHandlingService; private final QueryFactory queryFactory; private final UserScope userScope; private final KeycloakService keycloakService; + private final ActionConfirmationService actionConfirmationService; + private final NotificationProperties notificationProperties; + private final NotificationIntegrationEventHandler eventHandler; @Autowired public UserServiceImpl( @@ -98,8 +109,8 @@ public class UserServiceImpl implements UserService { MessageSource messageSource, EventBroker eventBroker, JsonHandlingService jsonHandlingService, - QueryFactory queryFactory, - UserScope userScope, KeycloakService keycloakService) { + XmlHandlingService xmlHandlingService, QueryFactory queryFactory, + UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotificationIntegrationEventHandler eventHandler) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -109,9 +120,13 @@ public class UserServiceImpl implements UserService { this.messageSource = messageSource; this.eventBroker = eventBroker; this.jsonHandlingService = jsonHandlingService; + this.xmlHandlingService = xmlHandlingService; this.queryFactory = queryFactory; this.userScope = userScope; this.keycloakService = keycloakService; + this.actionConfirmationService = actionConfirmationService; + this.notificationProperties = notificationProperties; + this.eventHandler = eventHandler; } //region persist @@ -243,7 +258,7 @@ public class UserServiceImpl implements UserService { this.eventBroker.emit(new UserTouchedEvent(data.getId())); return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, User._id), data); } - + //region mine @Override @@ -324,4 +339,123 @@ public class UserServiceImpl implements UserService { } //endregion + + //notifications + public void sendMergeAccountConfirmation(String email) throws InvalidApplicationException, JAXBException { + ActionConfirmationPersist persist = new ActionConfirmationPersist(); + persist.setType(ActionConfirmationType.MergeAccount); + persist.setStatus(ActionConfirmationStatus.Requested); + persist.setToken(UUID.randomUUID().toString()); + persist.setEmailConfirmation(new EmailConfirmationPersist(email)); +// persist.setCreatedById(this.userScope.getUserIdSafe()); TODO + persist.setCreatedById(UUID.fromString("2c447092-ae88-40ab-ae7d-43b80b373a5f")); + persist.setExpiresAt(Instant.now().plusSeconds(Long.parseLong(this.notificationProperties.getEmailExpirationTimeSeconds()))); + this.actionConfirmationService.persist(persist, null); + + NotificationIntegrationEvent event = new NotificationIntegrationEvent(); + event.setUserId(UUID.fromString("2c447092-ae88-40ab-ae7d-43b80b373a5f")); + List contactPairs = new ArrayList<>(); + contactPairs.add(new ContactPair(ContactInfoType.Email, email)); + NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); + event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); + event.setContactTypeHint(NotificationContactType.EMAIL); + event.setNotificationType(UUID.fromString(notificationProperties.getMergeAccountConfirmation())); + NotificationFieldData data = new NotificationFieldData(); + List fieldInfoList = new ArrayList<>(); + fieldInfoList.add(new FieldInfo("{userName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(UUID.fromString("2c447092-ae88-40ab-ae7d-43b80b373a5f")).first().getName())); + fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, persist.getToken())); + fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.notificationProperties.getEmailExpirationTimeSeconds())))); + data.setFields(fieldInfoList); + event.setData(jsonHandlingService.toJsonSafe(data)); + eventHandler.handle(event); + } + + public void sendRemoveCredentialConfirmation(String email) throws InvalidApplicationException, JAXBException { + ActionConfirmationPersist persist = new ActionConfirmationPersist(); + persist.setType(ActionConfirmationType.RemoveCredential); + persist.setStatus(ActionConfirmationStatus.Requested); + persist.setToken(UUID.randomUUID().toString()); + + UserContactInfoEntity userContactInfo = this.queryFactory.query(UserContactInfoQuery.class).types(ContactInfoType.Email).userIds(UUID.fromString("d107dbad-c67f-418d-a930-f928a690dbfc")).values(email).first(); + if(userContactInfo == null){ + throw new InvalidApplicationException("Email does not exist in this user!"); + } + UserCredentialQuery query = this.queryFactory.query(UserCredentialQuery.class).userIds(UUID.fromString("d107dbad-c67f-418d-a930-f928a690dbfc")); + if (query == null || query.count() == 0){ + throw new InvalidApplicationException("This user don't have credential!"); + } + + persist.setEmailConfirmation(new EmailConfirmationPersist(email)); +// persist.setCreatedById(this.userScope.getUserIdSafe()); TODO + persist.setCreatedById(UUID.fromString("d107dbad-c67f-418d-a930-f928a690dbfc")); + persist.setExpiresAt(Instant.now().plusSeconds(Long.parseLong(this.notificationProperties.getEmailExpirationTimeSeconds()))); + this.actionConfirmationService.persist(persist, null); + + NotificationIntegrationEvent event = new NotificationIntegrationEvent(); + event.setUserId(UUID.fromString("d107dbad-c67f-418d-a930-f928a690dbfc")); + List contactPairs = new ArrayList<>(); + contactPairs.add(new ContactPair(ContactInfoType.Email, email)); + NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); + event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); + event.setContactTypeHint(NotificationContactType.EMAIL); + event.setNotificationType(UUID.fromString(notificationProperties.getRemoveCredentialConfirmation())); + NotificationFieldData data = new NotificationFieldData(); + List fieldInfoList = new ArrayList<>(); + fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, persist.getToken())); + fieldInfoList.add(new FieldInfo("{email}", DataType.String, email)); + fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.notificationProperties.getEmailExpirationTimeSeconds())))); + data.setFields(fieldInfoList); + event.setData(jsonHandlingService.toJsonSafe(data)); + eventHandler.handle(event); + } + + + private String secondsToTime(int seconds) { + int sec = seconds % 60; + int hour = seconds / 60; + int min = hour % 60; + hour = hour / 60; + return (hour + ":" + min + ":" + sec); + } + + public void confirmMergeAccount(String token) throws InvalidApplicationException { + ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.MergeAccount).isActive(IsActive.Active).first(); + + this.checkActionState(action); + + EmailConfirmationEntity emailConfirmation = this.xmlHandlingService.fromXmlSafe(EmailConfirmationEntity.class, action.getData()); + + action.setStatus(ActionConfirmationStatus.Accepted); + //TODO merge + this.entityManager.merge(action); + } + + public void confirmRemoveCredential(String token) throws InvalidApplicationException { + ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.RemoveCredential).isActive(IsActive.Active).first(); + + this.checkActionState(action); + + // EmailConfirmationEntity emailConfirmation = this.xmlHandlingService.fromXmlSafe(EmailConfirmationEntity.class, action.getData()); + UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).userIds(UUID.fromString("d107dbad-c67f-418d-a930-f928a690dbfc")).first(); //ToDO + if (userCredential == null){ + throw new InvalidApplicationException("This user does not have credential"); + } + this.deleterFactory.deleter(UserCredentialDeleter.class).deleteAndSaveByIds(List.of(userCredential.getId())); + + action.setStatus(ActionConfirmationStatus.Accepted); + this.entityManager.merge(action); + } + + private void checkActionState(ActionConfirmationEntity action) throws InvalidApplicationException { + if (action == null){ + throw new InvalidApplicationException("Token does not exist!"); + } + if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){ + throw new InvalidApplicationException("Account is already confirmed!"); + } + if (action.getExpiresAt().compareTo(Instant.now()) < 0){ + throw new InvalidApplicationException("Token has expired!"); + } + } + } \ No newline at end of file diff --git a/dmp-backend/notification-service/notification-web/src/main/resources/config/notification.yml b/dmp-backend/notification-service/notification-web/src/main/resources/config/notification.yml index e7ce3a74c..b16586741 100644 --- a/dmp-backend/notification-service/notification-web/src/main/resources/config/notification.yml +++ b/dmp-backend/notification-service/notification-web/src/main/resources/config/notification.yml @@ -155,11 +155,11 @@ notification: cipher-fields: [ ] - #mergeAccountConfirmation key: BFE68845-CB05-4C5A-A03D-29161A7C9660 - subject-path: classpath:notification_templates/mergeaccountconfirmation/email/subject.{language}.txt + subject-path: classpath:notification_templates/mergeacountconfirmation/email/subject.{language}.txt subject-field-options: mandatory: [ ] optional: [ ] - body-path: classpath:notification_templates/mergeaccountconfirmation/email/body.{language}.html + body-path: classpath:notification_templates/mergeacountconfirmation/email/body.{language}.html body-field-options: mandatory: [ "{userName}", "{installation-url}", "{confirmationToken}" ] optional: diff --git a/dmp-backend/notification-service/notification-web/target/classes/config/notification.yml b/dmp-backend/notification-service/notification-web/target/classes/config/notification.yml index e7ce3a74c..b16586741 100644 --- a/dmp-backend/notification-service/notification-web/target/classes/config/notification.yml +++ b/dmp-backend/notification-service/notification-web/target/classes/config/notification.yml @@ -155,11 +155,11 @@ notification: cipher-fields: [ ] - #mergeAccountConfirmation key: BFE68845-CB05-4C5A-A03D-29161A7C9660 - subject-path: classpath:notification_templates/mergeaccountconfirmation/email/subject.{language}.txt + subject-path: classpath:notification_templates/mergeacountconfirmation/email/subject.{language}.txt subject-field-options: mandatory: [ ] optional: [ ] - body-path: classpath:notification_templates/mergeaccountconfirmation/email/body.{language}.html + body-path: classpath:notification_templates/mergeacountconfirmation/email/body.{language}.html body-field-options: mandatory: [ "{userName}", "{installation-url}", "{confirmationToken}" ] optional: diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java index 4bd3941ae..0b90776d0 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java @@ -12,10 +12,12 @@ import eu.eudat.model.censorship.UserCensor; import eu.eudat.model.persist.UserPersist; import eu.eudat.model.persist.UserRolePatchPersist; import eu.eudat.model.result.QueryResult; +import eu.eudat.models.data.helpers.responses.ResponseItem; import eu.eudat.query.UserQuery; import eu.eudat.query.lookup.UserLookup; import eu.eudat.service.responseutils.ResponseUtilsService; import eu.eudat.service.user.UserService; +import eu.eudat.types.ApiMessageCode; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.censor.CensorFactory; @@ -31,6 +33,7 @@ import jakarta.xml.bind.JAXBException; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -244,4 +247,60 @@ public class UserController { this.auditService.track(AuditableAction.User_Delete, "id", id); //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); } + + @GetMapping("mine/merge-account-request/{email}") + @Transactional + public ResponseEntity mergeAccount(@PathVariable("email") String email) throws InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("merge account to user").And("email", email)); + + this.userTypeService.sendMergeAccountConfirmation(email); + +// this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries( +// new AbstractMap.SimpleEntry("model", model) +// )); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).payload("Merge Account Request Success")); + } + + @GetMapping("mine/confirm-merge-account/token/{token}") + @Transactional + public ResponseEntity confirmMergeAccount(@PathVariable("token") String token) throws InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("confirm merge account to user").And("token", token)); + + this.userTypeService.confirmMergeAccount(token); + +// this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries( +// new AbstractMap.SimpleEntry("model", model) +// )); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).payload("Merge Account Confirm Success")); + } + + @GetMapping("mine/remove-credential-request/{email}") + @Transactional + public ResponseEntity removeCredentialAccount(@PathVariable("email") String email) throws InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("remove credential request to user").And("email", email)); + + this.userTypeService.sendRemoveCredentialConfirmation(email); + +// this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries( +// new AbstractMap.SimpleEntry("model", model) +// )); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).payload("Remove Credential Request Success")); + } + + @GetMapping("mine/confirm-remove-credential/token/{token}") + @Transactional + public ResponseEntity confirmRemoveCredentialAccount(@PathVariable("token") String token) throws InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("confirm remove credential to user").And("token", token)); + + this.userTypeService.confirmRemoveCredential(token); + +// this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries( +// new AbstractMap.SimpleEntry("model", model) +// )); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).payload("Remove Credential Account Success")); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index ee6683be0..6a83c202e 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -19,7 +19,7 @@ import eu.eudat.commons.enums.old.notification.NotifyState; import eu.eudat.data.query.items.table.dataset.DatasetTableRequest; import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem; import eu.eudat.data.query.items.table.dmp.DataManagementPlanTableRequest; -import eu.eudat.depositinterface.models.DmpDepositModel; +//import eu.eudat.depositinterface.models.DmpDepositModel; import eu.eudat.exceptions.datamanagementplan.DMPNewVersionException; import eu.eudat.exceptions.datamanagementplan.DMPWithDatasetsDeleteException; import eu.eudat.exceptions.security.ForbiddenException; @@ -2583,14 +2583,14 @@ public class DataManagementPlanManager { FileEnvelope file = getWordDocument(depositRequest.getDmpId().toString(), configLoader); String name = file.getFilename().substring(0, file.getFilename().length() - 5).replaceAll("[^a-zA-Z0-9_+ ]", "").replace(" ", "_").replace(",", "_"); byte[] pdfFile = null; //PDFUtils.convertToPDF(file, environment); //TODO - eu.eudat.depositinterface.models.FileEnvelope pdfEnvelope = new eu.eudat.depositinterface.models.FileEnvelope(); - pdfEnvelope.setFile(pdfFile); - pdfEnvelope.setFilename(name + ".pdf"); - eu.eudat.depositinterface.models.FileEnvelope rdaJsonFile = new eu.eudat.depositinterface.models.FileEnvelope(); +// eu.eudat.depositinterface.models.FileEnvelope pdfEnvelope = new eu.eudat.depositinterface.models.FileEnvelope(); +// pdfEnvelope.setFile(pdfFile); +// pdfEnvelope.setFilename(name + ".pdf"); +// eu.eudat.depositinterface.models.FileEnvelope rdaJsonFile = new eu.eudat.depositinterface.models.FileEnvelope(); try { FileEnvelope rdaJsonDocument = getRDAJsonDocument(depositRequest.getDmpId().toString()); //rdaJsonFile.setFile(rdaJsonDocument.getFile()); //TODO - rdaJsonFile.setFilename(rdaJsonDocument.getFilename()); +// rdaJsonFile.setFilename(rdaJsonDocument.getFilename()); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -2598,7 +2598,7 @@ public class DataManagementPlanManager { File supportingFilesZip = this.createSupportingFilesZip(dmp); - DmpDepositModel dmpDepositModel = new DmpDepositModel(); // DMPToDepositMapper.fromDMP(dmp, pdfEnvelope, rdaJsonFile, supportingFilesZip, previousDOI);//TODO +// DmpDepositModel dmpDepositModel = new DmpDepositModel(); // DMPToDepositMapper.fromDMP(dmp, pdfEnvelope, rdaJsonFile, supportingFilesZip, previousDOI);//TODO String finalDoi = null; try { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java index 36a2f1388..6ac269716 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java @@ -29,7 +29,6 @@ import java.util.*; public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { private static final Logger logger = LoggerFactory.getLogger(ConfirmationEmailServiceImpl.class); //private Logger logger; - private Environment environment; private final UserScope userScope; private final QueryFactory queryFactory; private final JsonHandlingService jsonHandlingService; @@ -37,9 +36,8 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { private final NotificationProperties notificationProperties; - public ConfirmationEmailServiceImpl(/*Logger logger,*/ Environment environment, UserScope userScope, QueryFactory queryFactory, JsonHandlingService jsonHandlingService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties) { + public ConfirmationEmailServiceImpl(/*Logger logger,*/ UserScope userScope, QueryFactory queryFactory, JsonHandlingService jsonHandlingService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties) { // this.logger = logger; - this.environment = environment; this.userScope = userScope; this.queryFactory = queryFactory; this.jsonHandlingService = jsonHandlingService; @@ -55,30 +53,13 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { confirmationEmail.setExpiresAt(Date .from(new Date() .toInstant() - .plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds"))) + .plusSeconds(Long.parseLong(this.notificationProperties.getEmailExpirationTimeSeconds())) ) ); confirmationEmail.setUserId(user.getId()); confirmationEmail.setIsConfirmed(false); confirmationEmail.setToken(UUID.randomUUID()); confirmationEmail = loginConfirmationEmailDao.createOrUpdate(confirmationEmail); - - NotificationIntegrationEvent event = new NotificationIntegrationEvent(); - event.setUserId(userScope.getUserIdSafe()); - List contactPairs = new ArrayList<>(); - contactPairs.add(new ContactPair(ContactInfoType.Email, email)); - NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); - event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); - event.setContactTypeHint(NotificationContactType.EMAIL); - event.setNotificationType(UUID.fromString(notificationProperties.getConfirmation())); - NotificationFieldData data = new NotificationFieldData(); - List fieldInfoList = new ArrayList<>(); - fieldInfoList.add(new FieldInfo("{host}", DataType.String, this.environment.getProperty("dmp.domain"))); - fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, confirmationEmail.getToken().toString())); - fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.environment.getProperty("conf_email.expiration_time_seconds"))))); - data.setFields(fieldInfoList); - event.setData(jsonHandlingService.toJsonSafe(data)); - eventHandler.handle(event); } @Override @@ -90,7 +71,7 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { confirmationEmail.setExpiresAt(Date .from(new Date() .toInstant() - .plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds"))) + .plusSeconds(Long.parseLong(this.notificationProperties.getEmailExpirationTimeSeconds())) ) ); confirmationEmail.setUserId(user.getId()); @@ -113,13 +94,12 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); event.setContactTypeHint(NotificationContactType.EMAIL); - event.setNotificationType(UUID.fromString(notificationProperties.getUnlinkConfirmation())); + event.setNotificationType(UUID.fromString(notificationProperties.getRemoveCredentialConfirmation())); NotificationFieldData data = new NotificationFieldData(); List fieldInfoList = new ArrayList<>(); - fieldInfoList.add(new FieldInfo("{host}", DataType.String, this.environment.getProperty("dmp.domain"))); fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, confirmationEmail.getToken().toString())); fieldInfoList.add(new FieldInfo("{email}", DataType.String, email)); - fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.environment.getProperty("conf_email.expiration_time_seconds"))))); + fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.notificationProperties.getEmailExpirationTimeSeconds())))); data.setFields(fieldInfoList); event.setData(jsonHandlingService.toJsonSafe(data)); eventHandler.handle(event); @@ -133,7 +113,7 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { confirmationEmail.setExpiresAt(Date .from(new Date() .toInstant() - .plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds"))) + .plusSeconds(Long.parseLong(this.notificationProperties.getEmailExpirationTimeSeconds())) ) ); confirmationEmail.setUserId(user.getId()); @@ -156,13 +136,12 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); event.setContactTypeHint(NotificationContactType.EMAIL); - event.setNotificationType(UUID.fromString(notificationProperties.getMergeConfirmation())); + event.setNotificationType(UUID.fromString(notificationProperties.getMergeAccountConfirmation())); NotificationFieldData data = new NotificationFieldData(); List fieldInfoList = new ArrayList<>(); fieldInfoList.add(new FieldInfo("{userName}", DataType.String, user.getName())); - fieldInfoList.add(new FieldInfo("{host}", DataType.String, this.environment.getProperty("dmp.domain"))); fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, confirmationEmail.getToken().toString())); - fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.environment.getProperty("conf_email.expiration_time_seconds"))))); + fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(Integer.parseInt(this.notificationProperties.getEmailExpirationTimeSeconds())))); data.setFields(fieldInfoList); event.setData(jsonHandlingService.toJsonSafe(data)); eventHandler.handle(event); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java index fce27ed9d..acff0b722 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java @@ -105,7 +105,7 @@ public class InvitationServiceImpl implements InvitationService { public void sendInvitation(DmpEntity dmp, Invitation invitation, UserEntity user, Integer role) throws InvalidApplicationException { NotificationIntegrationEvent event = new NotificationIntegrationEvent(); - event.setUserId(userScope.getUserIdSafe()); + event.setUserId(user.getId()); UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId()); query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal)); @@ -115,12 +115,11 @@ public class InvitationServiceImpl implements InvitationService { NotificationContactData contactData = new NotificationContactData(contactPairs, null, null); event.setContactHint(jsonHandlingService.toJsonSafe(contactData)); event.setContactTypeHint(NotificationContactType.EMAIL); - event.setNotificationType(UUID.fromString(notificationProperties.getDataManagementPlan())); + event.setNotificationType(UUID.fromString(notificationProperties.getDmpInvitation())); NotificationFieldData data = new NotificationFieldData(); List fieldInfoList = new ArrayList<>(); fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName())); fieldInfoList.add(new FieldInfo("{invitationID}", DataType.String, invitation.getId().toString())); - fieldInfoList.add(new FieldInfo("{host}", DataType.String, this.environment.getProperty("dmp.domain"))); fieldInfoList.add(new FieldInfo("{dmpname}", DataType.String, dmp.getLabel())); fieldInfoList.add(new FieldInfo("{dmprole}", DataType.String, DmpUserRole.of(role.shortValue()).toString())); data.setFields(fieldInfoList); diff --git a/dmp-db-scema/updates/00.01.040_addActionConfirmation.sql b/dmp-db-scema/updates/00.01.040_addActionConfirmation.sql new file mode 100644 index 000000000..6f3523a40 --- /dev/null +++ b/dmp-db-scema/updates/00.01.040_addActionConfirmation.sql @@ -0,0 +1,35 @@ +DO $$DECLARE + this_version CONSTANT varchar := '00.01.040'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + CREATE TABLE public."ActionConfirmation" + ( + id uuid NOT NULL, + type smallint NOT NULL, + status smallint NOT NULL, + token character varying NOT NULL, + data text NOT NULL, + expires_at timestamp without time zone NOT NULL, + created_by uuid NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + is_active smallint NOT NULL, + tenant uuid, + CONSTRAINT "ActionConfirmation_pkey" PRIMARY KEY (id), + CONSTRAINT "ActionConfirmation_created_by_fkey" FOREIGN KEY (created_by) + REFERENCES public."User" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID, + CONSTRAINT "ActionConfirmation_tenant_fkey" FOREIGN KEY (tenant) + REFERENCES public."Tenant" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID + ); + + INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.040', '2023-12-13 12:00:00.000000+02', now(), 'Add ActionConfirmation table.'); + +END$$; \ No newline at end of file