Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue email to all approvers #1258

Merged
merged 4 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}