Skip to content

Commit

Permalink
Issue email to all approvers (#1258)
Browse files Browse the repository at this point in the history
* Send an Approval email to everyone that is able to approve the request.

Signed-off-by: Aindriu Lavelle <aindriu.lavelle@aiven.io>

* remove redundant import

Signed-off-by: Aindriu Lavelle <aindriu.lavelle@aiven.io>

* Add unit tests for MailUtils

Signed-off-by: Aindriu Lavelle <aindriu.lavelle@aiven.io>

---------

Signed-off-by: Aindriu Lavelle <aindriu.lavelle@aiven.io>
  • Loading branch information
aindriu-aiven authored May 19, 2023
1 parent cd0c434 commit f1171c1
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
Expand Down
19 changes: 18 additions & 1 deletion core/src/main/java/io/aiven/klaw/service/EmailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -71,10 +72,21 @@ public class EmailService {
+ "\t</tr>\n"
+ "</table></html>";

@Async("notificationsThreadPool")
public void sendSimpleMessage(
String to, String cc, String subject, String text, int tenantId, String loginUrl) {

sendSimpleMessage(to, cc, null, subject, text, tenantId, loginUrl);
}

@Async("notificationsThreadPool")
public void sendSimpleMessage(
String to,
String cc,
List<String> bcc,
String subject,
String text,
int tenantId,
String loginUrl) {
String emailNotificationsEnabled =
manageDatabase.getKwPropertyValue(EMAIL_NOTIFICATIONS_ENABLED_KEY, DEFAULT_TENANT_ID);
try {
Expand All @@ -83,6 +95,11 @@ public void sendSimpleMessage(
if (cc != null) {
message.setRecipients(Message.RecipientType.CC, cc);
}
if (bcc != null && !bcc.isEmpty()) {
for (String bccAddress : bcc) {
message.setRecipients(Message.RecipientType.BCC, bccAddress);
}
}

message.setSubject(subject);
Address address = new InternetAddress(noReplyMailId);
Expand Down
74 changes: 68 additions & 6 deletions core/src/main/java/io/aiven/klaw/service/MailUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
import io.aiven.klaw.helpers.UtilMethods;
import io.aiven.klaw.model.enums.ApiResultStatus;
import io.aiven.klaw.model.enums.MailType;
import io.aiven.klaw.model.enums.PermissionType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -109,19 +112,22 @@ void sendMail(
getUserName(SecurityContextHolder.getContext().getAuthentication().getPrincipal()))
.getTenantId();
loadKwProps(tenantId);

Boolean requiresApproval = false;
switch (mailType) {
case TOPIC_CREATE_REQUESTED -> {
formattedStr = String.format(topicRequestMail, "'" + topicName + "'");
subject = "Create Topic Request";
requiresApproval = true;
}
case TOPIC_DELETE_REQUESTED -> {
formattedStr = String.format(topicDeleteRequestMail, "'" + topicName + "'");
subject = "Delete Topic Request";
requiresApproval = true;
}
case TOPIC_CLAIM_REQUESTED -> {
formattedStr = String.format(topicClaimRequestMail, "'" + topicName + "'");
subject = "Claim Topic Request";
requiresApproval = true;
}
case TOPIC_REQUEST_APPROVED -> {
formattedStr = String.format(topicRequestApproved, "'" + topicName + "'");
Expand All @@ -135,10 +141,12 @@ void sendMail(
case ACL_REQUESTED -> {
formattedStr = String.format(aclRequestMail, "'" + acl + "'", "'" + topicName + "'");
subject = "New Acl Request";
requiresApproval = true;
}
case ACL_DELETE_REQUESTED -> {
formattedStr = String.format(aclDeleteRequestMail, "'" + acl + "'", "'" + topicName + "'");
subject = "Acl Delete Request";
requiresApproval = true;
}
case ACL_REQUEST_APPROVED -> {
formattedStr = String.format(aclRequestApproved, "'" + acl + "'", "'" + topicName + "'");
Expand All @@ -157,9 +165,25 @@ void sendMail(
formattedStr = "Acl Request processing failed : " + acl + ", " + topicName;
subject = "Request processing failed.";
}
case CONNECTOR_CLAIM_REQUESTED,
CONNECTOR_REQUEST_DENIED,
CONNECTOR_DELETE_REQUESTED,
SCHEMA_REQUESTED -> {
// all remaining requests that require approvals are grouped here.
requiresApproval = true;
}
}

sendMail(username, dbHandle, formattedStr, subject, false, null, tenantId, loginUrl);
sendMail(
username,
dbHandle,
formattedStr,
subject,
false,
requiresApproval,
null,
tenantId,
loginUrl);
}

void sendMail(String username, String pwd, HandleDbRequests dbHandle, String loginUrl) {
Expand All @@ -174,7 +198,7 @@ void sendMail(String username, String pwd, HandleDbRequests dbHandle, String log
formattedStr = String.format(newUserAdded, username, pwd);
subject = "Access to Klaw";

sendMail(username, dbHandle, formattedStr, subject, false, null, tenantId, loginUrl);
sendMail(username, dbHandle, formattedStr, subject, false, false, null, tenantId, loginUrl);
}

void sendMailResetPwd(
Expand Down Expand Up @@ -255,6 +279,7 @@ void sendMailRegisteredUserSaas(
formattedStr,
subject,
true,
false,
registerUserInfo.getMailid(),
tenantId,
loginUrl);
Expand Down Expand Up @@ -296,6 +321,7 @@ void sendMailRegisteredUser(
formattedStr,
subject,
true,
false,
registerUserInfo.getMailid(),
tenantId,
loginUrl);
Expand Down Expand Up @@ -340,6 +366,7 @@ private void sendMail(
String formattedStr,
String subject,
boolean registrationRequest,
boolean requiresApproval,
String otherMailId,
int tenantId,
String loginUrl) {
Expand All @@ -349,6 +376,8 @@ private void sendMail(
String emailId;

String emailIdTeam = null;
Integer teamId = null;
List<String> allApprovers = null;
try {
if (registrationRequest) {
emailId = otherMailId;
Expand All @@ -358,14 +387,19 @@ private void sendMail(

try {
List<Team> allTeams = dbHandle.getAllTeamsOfUsers(username, tenantId);
if (!allTeams.isEmpty()) emailIdTeam = allTeams.get(0).getTeammail();
if (!allTeams.isEmpty()) {
emailIdTeam = allTeams.get(0).getTeammail();
teamId = allTeams.get(0).getTeamId();
}
} catch (Exception e) {
log.error("Exception :", e);
}

if (requiresApproval) {
allApprovers = getAllUsersWithPermissionToApproveRequest(tenantId, username, teamId);
}
if (emailId != null) {
emailService.sendSimpleMessage(
emailId, emailIdTeam, subject, formattedStr, tenantId, loginUrl);
emailId, emailIdTeam, allApprovers, subject, formattedStr, tenantId, loginUrl);
} else {
log.error("Email id not found. Notification not sent !!");
}
Expand Down Expand Up @@ -417,6 +451,34 @@ public String getEmailAddressFromUsername(String username) {
}
}

private List<String> getAllUsersWithPermissionToApproveRequest(
int tenantId, String username, Integer teamId) {

Map<String, List<String>> rolesToPermissions =
manageDatabase.getRolesPermissionsPerTenant(tenantId);

List<String> roles = new ArrayList<>();

rolesToPermissions.forEach(
(k, v) -> {
if (v.contains(PermissionType.APPROVE_ALL_REQUESTS_TEAMS.name())) {
roles.add(k);
}
});

// Prevent duplicates only show from the correct tenant, that is not the usernae of the
// requestor and that is not on the same team as they have already received that email.
return manageDatabase.selectAllCachedUserInfo().stream()
.filter(
user ->
user.getTenantId() == tenantId
&& !user.getUsername().equals(username)
&& roles.contains(user.getRole())
&& (teamId == null || !user.getTeamId().equals(teamId)))
.map(u -> u.getMailid())
.toList();
}

public String sendMailToSaasAdmin(int tenantId, String userName, String period, String loginUrl) {
String mailtext =
"Tenant extension : Tenant " + tenantId + " username " + userName + " period " + period;
Expand Down
111 changes: 101 additions & 10 deletions core/src/test/java/io/aiven/klaw/service/MailUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@

import io.aiven.klaw.config.ManageDatabase;
import io.aiven.klaw.dao.UserInfo;
import io.aiven.klaw.helpers.HandleDbRequests;
import io.aiven.klaw.helpers.KwConstants;
import io.aiven.klaw.helpers.db.rdbms.HandleDbRequestsJdbc;
import io.aiven.klaw.model.enums.MailType;
import io.aiven.klaw.model.enums.PermissionType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
Expand All @@ -25,7 +30,9 @@ public class MailUtilsTest {
public static final String LOGIN_URL = "https://localhost:9097";
@Mock UserDetails userDetails;

@Mock HandleDbRequests handleDbRequests;
// @Mock HandleDbRequests handleDbRequests;

@Mock HandleDbRequestsJdbc handleDbRequestsJdbc;

@Mock EmailService emailService;

Expand All @@ -36,6 +43,11 @@ public class MailUtilsTest {
@BeforeEach
public void setUp() throws Exception {
// mailService = new MailUtils();
when(manageDatabase.getHandleDbRequests()).thenReturn(handleDbRequestsJdbc);
when(handleDbRequestsJdbc.getUsersInfo(eq("james")))
.thenReturn(createUserInfo("James", "USER"));
when(manageDatabase.getRolesPermissionsPerTenant(eq(101)))
.thenReturn(getRolesToPermissionsMap());
}

@Test
Expand All @@ -45,13 +57,11 @@ public void getUserDetails() {}
public void resetPasswordEmail_noCCTeam() throws InterruptedException {

String username = "Octopus";
UserInfo info = new UserInfo();
info.setUsername(username);
info.setMailid("Octopus.klaw@mailid");
UserInfo info = createUserInfo(username, "USER");
when(manageDatabase.selectAllCachedUserInfo()).thenReturn(List.of(info));
when(manageDatabase.getKwPropertyValue(eq("klaw.mail.passwordreset.content"), eq(101)))
.thenReturn(KwConstants.MAIL_PASSWORDRESET_CONTENT);
mailService.sendMailResetPwd(username, "KlawPassword", handleDbRequests, 101, LOGIN_URL);
mailService.sendMailResetPwd(username, "KlawPassword", handleDbRequestsJdbc, 101, LOGIN_URL);

Thread.sleep(1000);
Mockito.verify(emailService, timeout(1000).times(1))
Expand All @@ -63,15 +73,96 @@ public void resetPasswordEmail_noCCTeam() throws InterruptedException {
public void resetPasswordEmail_noSuchUser() {

String username = "Octopus";
UserInfo info = new UserInfo();
info.setUsername("Octi");
info.setMailid("Octi.klaw@mailid");
UserInfo info = createUserInfo("Octi", "USER");
when(manageDatabase.selectAllCachedUserInfo()).thenReturn(List.of(info));
when(manageDatabase.getKwPropertyValue(eq("klaw.mail.passwordreset.content"), eq(101)))
.thenReturn(KwConstants.MAIL_PASSWORDRESET_CONTENT);
mailService.sendMailResetPwd(username, "KlawPassword", handleDbRequests, 101, LOGIN_URL);
mailService.sendMailResetPwd(username, "KlawPassword", handleDbRequestsJdbc, 101, LOGIN_URL);
Mockito.verify(emailService, timeout(1000).times(0))
.sendSimpleMessage(
eq(info.getMailid()), eq(null), anyString(), anyString(), eq(101), eq(LOGIN_URL));
}

@Test
@WithMockUser(
username = "james",
authorities = {"USER"})
public void sendMailWith_NoBCC() {
String username = "James";
when(manageDatabase.selectAllCachedUserInfo())
.thenReturn(List.of(createUserInfo(username, "USER"), createUserInfo("Tom", "ADMIN")));
when(manageDatabase.getKwPropertyValue(eq("klaw.mail.aclrequestapproval.content"), eq(101)))
.thenReturn(KwConstants.MAIL_ACLREQUESTAPPROVAL_CONTENT);

mailService.sendMail(
"TopicOne",
"AclOne",
null,
username,
handleDbRequestsJdbc,
MailType.ACL_REQUEST_APPROVED,
LOGIN_URL);

Mockito.verify(emailService, timeout(1000).times(1))
.sendSimpleMessage(
anyString(), eq(null), eq(null), anyString(), anyString(), eq(101), eq(LOGIN_URL));
}

@Test
@WithMockUser(
username = "james",
authorities = {"USER"})
public void sendMailWith_BCC() {

String username = "James";
when(manageDatabase.selectAllCachedUserInfo())
.thenReturn(List.of(createUserInfo(username, "USER"), createUserInfo("Tom", "ADMIN")));
when(manageDatabase.getKwPropertyValue(eq("klaw.mail.topicrequest.content"), eq(101)))
.thenReturn(KwConstants.MAIL_TOPICREQUEST_CONTENT);

mailService.sendMail(
"TopicOne",
"AclOne",
null,
username,
handleDbRequestsJdbc,
MailType.TOPIC_CREATE_REQUESTED,
LOGIN_URL);

Mockito.verify(emailService, timeout(1000).times(1))
.sendSimpleMessage(
anyString(),
eq(null),
eq(List.of("Tom.klaw@mailid")),
anyString(),
anyString(),
eq(101),
eq(LOGIN_URL));
}

private Map<String, List<String>> getRolesToPermissionsMap() {

List<String> user = List.of(PermissionType.APPROVE_TOPICS.name());

List<String> admin =
List.of(
PermissionType.APPROVE_TOPICS.name(), PermissionType.APPROVE_ALL_REQUESTS_TEAMS.name());

return new HashMap<>() {
{
put("USER", user);
put("ADMIN", admin);
}
};
}

private static UserInfo createUserInfo(String username, String role) {
UserInfo info = new UserInfo();
info.setUsername(username);
info.setMailid(username + ".klaw@mailid");
info.setTenantId(101);
info.setTeamId(10);
info.setRole(role);
return info;
}
}

0 comments on commit f1171c1

Please sign in to comment.