-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Notify the user via email about granted project access (#365)
After a user has been granted access to a project, they will now get notified with an email. The email also contains a link to the project to simplify access for the user.
- Loading branch information
Showing
11 changed files
with
301 additions
and
1 deletion.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
authorization/src/main/java/life/qbic/authorization/application/AppContextProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package life.qbic.authorization.application; | ||
|
||
/** | ||
* <b>App Context Provider</b> | ||
* <p> | ||
* Provides some utility methods to create navigation targets within the application. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
public interface AppContextProvider { | ||
|
||
/** | ||
* Returns a resolvable URL to the target project resource in the application. | ||
* | ||
* @param projectId the project id as the target web resource | ||
* @return a fully resolvable URL | ||
* @since 1.0.0 | ||
*/ | ||
String urlToProject(String projectId); | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
.../src/main/java/life/qbic/authorization/application/policy/ProjectAccessGrantedPolicy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package life.qbic.authorization.application.policy; | ||
|
||
import java.util.Objects; | ||
import life.qbic.authorization.application.policy.directive.InformUserAboutGrantedAccess; | ||
import life.qbic.domain.concepts.DomainEventDispatcher; | ||
|
||
/** | ||
* <b>Policy: Project access granted</b> | ||
* <p> | ||
* Business policy that needs to be executed after a project access has been granted. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
public class ProjectAccessGrantedPolicy { | ||
|
||
private InformUserAboutGrantedAccess informUserAboutGrantedAccess; | ||
|
||
public ProjectAccessGrantedPolicy(InformUserAboutGrantedAccess informUserAboutGrantedAccess) { | ||
this.informUserAboutGrantedAccess = Objects.requireNonNull(informUserAboutGrantedAccess); | ||
DomainEventDispatcher.instance().subscribe(informUserAboutGrantedAccess); | ||
} | ||
|
||
} |
77 changes: 77 additions & 0 deletions
77
...va/life/qbic/authorization/application/policy/directive/InformUserAboutGrantedAccess.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package life.qbic.authorization.application.policy.directive; | ||
|
||
import java.util.Objects; | ||
import life.qbic.authentication.domain.user.concept.User; | ||
import life.qbic.authentication.domain.user.concept.UserId; | ||
import life.qbic.authentication.domain.user.repository.UserRepository; | ||
import life.qbic.authorization.application.AppContextProvider; | ||
import life.qbic.domain.concepts.DomainEvent; | ||
import life.qbic.domain.concepts.DomainEventSubscriber; | ||
import life.qbic.domain.concepts.communication.EmailService; | ||
import life.qbic.projectmanagement.domain.project.service.event.ProjectAccessGranted; | ||
import org.jobrunr.jobs.annotations.Job; | ||
import org.jobrunr.scheduling.JobScheduler; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* <b>Directive: Inform user about granted access</b> | ||
* <p> | ||
* Notifies the user via email about the recently granted project access. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
@Component | ||
public class InformUserAboutGrantedAccess implements DomainEventSubscriber<ProjectAccessGranted> { | ||
|
||
private final EmailService emailService; | ||
|
||
private final JobScheduler jobScheduler; | ||
private final UserRepository userRepository; | ||
|
||
private final AppContextProvider appContextProvider; | ||
|
||
public InformUserAboutGrantedAccess(EmailService emailService, JobScheduler jobScheduler, | ||
UserRepository userRepository, AppContextProvider appContextProvider) { | ||
this.emailService = Objects.requireNonNull(emailService); | ||
this.jobScheduler = Objects.requireNonNull(jobScheduler); | ||
this.userRepository = Objects.requireNonNull(userRepository); | ||
this.appContextProvider = Objects.requireNonNull(appContextProvider); | ||
} | ||
|
||
private String composeMessage(String projectId, User recipient, String projectTitle) { | ||
return String.format(""" | ||
Dear %s, | ||
you have been granted access to project: | ||
'%s' | ||
Please click the link below to access the project after login: | ||
%s | ||
Need help? Contact us for further questions at [email protected] | ||
Best regards,\ | ||
The QBiC team | ||
""", recipient.fullName(), projectTitle, appContextProvider.urlToProject(projectId)); | ||
} | ||
|
||
@Override | ||
public Class<? extends DomainEvent> subscribedToEventType() { | ||
return ProjectAccessGranted.class; | ||
} | ||
|
||
@Override | ||
public void handleEvent(ProjectAccessGranted event) { | ||
jobScheduler.enqueue(() -> notifyUser(event.forUserId(), event.forProjectId(), event.forProjectTitle())); | ||
} | ||
|
||
@Job(name = "Notify user about granted project access") | ||
public void notifyUser(String userId, String projectId, String projectTitle) | ||
throws RuntimeException { | ||
var recipient = userRepository.findById(UserId.from(userId)).get(); | ||
emailService.send(recipient.emailAddress().get(), recipient.fullName().get(), | ||
"Project access granted", composeMessage(projectId, recipient, projectTitle)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
import life.qbic.application.commons.ApplicationResponse; | ||
import life.qbic.domain.concepts.communication.Email; | ||
import life.qbic.domain.concepts.communication.EmailService; | ||
import life.qbic.domain.concepts.communication.Recipient; | ||
import life.qbic.logging.api.Logger; | ||
import life.qbic.logging.service.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Value; | ||
|
@@ -78,6 +79,16 @@ public void send(Email email) { | |
} | ||
} | ||
|
||
@Override | ||
public void send(String recipientAddress, String recipientFullName, String subject, String message) { | ||
var email = new Email(message, subject, "[email protected]", | ||
new Recipient(recipientAddress, recipientFullName), "text/plain"); | ||
sendPlainEmail(email).ifSuccessOrElse( | ||
successResponse -> reportSuccess(successResponse, email), | ||
failureResponse -> reportFailure(failureResponse, email) | ||
); | ||
} | ||
|
||
private void reportSuccess(ApplicationResponse applicationResponse, Email email) { | ||
String emailSendSuccessMessage = String.format( | ||
"Email with subject '%s' successfully send to '%s'", | ||
|
43 changes: 43 additions & 0 deletions
43
...src/main/java/life/qbic/projectmanagement/domain/project/service/AccessDomainService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package life.qbic.projectmanagement.domain.project.service; | ||
|
||
import java.util.Objects; | ||
import life.qbic.domain.concepts.DomainEventDispatcher; | ||
import life.qbic.projectmanagement.domain.project.ProjectId; | ||
import life.qbic.projectmanagement.domain.project.repository.ProjectRepository; | ||
import life.qbic.projectmanagement.domain.project.service.event.ProjectAccessGranted; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
/** | ||
* <b>Access Domain Service</b> | ||
* <p> | ||
* Service that will emit domain events for project access related tasks, that are not part of | ||
* domain aggregates. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
@Service | ||
public class AccessDomainService { | ||
|
||
private final ProjectRepository projectRepository; | ||
|
||
@Autowired | ||
public AccessDomainService(ProjectRepository projectRepository) { | ||
this.projectRepository = Objects.requireNonNull(projectRepository); | ||
} | ||
|
||
/** | ||
* Inform the domain service, that a user has been granted with access for a certain project. | ||
* | ||
* @param projectId the project id of the affected project | ||
* @param userId the user that has been granted with access for the project | ||
* @since 1.0.0 | ||
*/ | ||
public void grantProjectAccessFor(String projectId, String userId) { | ||
var projectTitle = projectRepository.find(ProjectId.parse(projectId)).get().getProjectIntent() | ||
.projectTitle().title(); | ||
var projectAccessGranted = ProjectAccessGranted.create(userId, projectId, projectTitle); | ||
DomainEventDispatcher.instance().dispatch(projectAccessGranted); | ||
} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
...n/java/life/qbic/projectmanagement/domain/project/service/event/ProjectAccessGranted.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package life.qbic.projectmanagement.domain.project.service.event; | ||
|
||
import java.io.Serial; | ||
import java.time.Instant; | ||
import java.util.Objects; | ||
import life.qbic.domain.concepts.DomainEvent; | ||
|
||
/** | ||
* <b>Project Access Granted Event</b> | ||
* | ||
* <p>This event is emitted after access has been granted to a user</p> | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
public class ProjectAccessGranted extends DomainEvent { | ||
|
||
@Serial | ||
private static final long serialVersionUID = 199678646014632540L; | ||
private final String userId; | ||
private final String projectId; | ||
|
||
private final String projectTitle; | ||
|
||
private final Instant occurredOn; | ||
|
||
private ProjectAccessGranted(Instant occurredOn, String userId, String projectId, | ||
String projectTitle) { | ||
this.userId = Objects.requireNonNull(userId); | ||
this.projectId = Objects.requireNonNull(projectId); | ||
this.projectTitle = Objects.requireNonNull(projectTitle); | ||
this.occurredOn = occurredOn; | ||
} | ||
|
||
public static ProjectAccessGranted create(String userId, String projectId, String projectTitle) { | ||
return new ProjectAccessGranted(Instant.now(), userId, projectId, projectTitle); | ||
} | ||
|
||
@Override | ||
public Instant occurredOn() { | ||
return this.occurredOn; | ||
} | ||
|
||
public String forUserId() { | ||
return userId; | ||
} | ||
|
||
public String forProjectId() { | ||
return projectId; | ||
} | ||
|
||
public String forProjectTitle() { | ||
return projectTitle; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
vaadinfrontend/src/main/java/life/qbic/datamanager/DataManagerContextProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package life.qbic.datamanager; | ||
|
||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import life.qbic.authorization.application.AppContextProvider; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* <b>Data Manager context provider</b> | ||
* <p> | ||
* Simple implementation of the {@link AppContextProvider} interface. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
@Component | ||
public class DataManagerContextProvider implements AppContextProvider { | ||
|
||
private final String protocol; | ||
private final String host; | ||
private final int port; | ||
private final String context; | ||
private final String endpoint; | ||
|
||
public DataManagerContextProvider( | ||
@Value("${service.host.protocol}") String protocol, | ||
@Value("${service.host.name}") String host, | ||
@Value("${service.host.port}") int port, | ||
@Value("${server.servlet.context-path}") String contextPath, | ||
@Value("${project-endpoint}") String projectEndpoint) { | ||
this.protocol = protocol; | ||
this.host = host; | ||
this.port = port; | ||
this.context = contextPath; | ||
this.endpoint = projectEndpoint; | ||
} | ||
|
||
@Override | ||
public String urlToProject(String projectId) { | ||
var fullPath = context + endpoint + "/" + projectId; | ||
try { | ||
return new URL(protocol, host, port, fullPath).toExternalForm(); | ||
} catch (MalformedURLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters