Skip to content

Commit 4d5a755

Browse files
authored
Merge pull request #4053 from dBucik/group_admin_notif_cascade
feat: 🎸 Cascade to parent gr. when deciding gr. TO recipients
2 parents 232d92b + 8adea84 commit 4d5a755

File tree

1 file changed

+155
-83
lines changed

1 file changed

+155
-83
lines changed

perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java

Lines changed: 155 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
package cz.metacentrum.perun.registrar.impl;
22

3-
import java.io.*;
3+
import java.io.IOException;
4+
import java.io.UnsupportedEncodingException;
45
import java.math.BigInteger;
56
import java.net.URLEncoder;
67
import java.nio.charset.StandardCharsets;
7-
import java.util.*;
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.Collections;
11+
import java.util.HashMap;
12+
import java.util.HashSet;
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Locale;
16+
import java.util.Map;
17+
import java.util.Properties;
18+
import java.util.Set;
819
import java.util.regex.Matcher;
920
import java.util.regex.Pattern;
1021

@@ -32,8 +43,31 @@
3243
import cz.metacentrum.perun.audit.events.MailManagerEvents.MailForVoIdUpdated;
3344
import cz.metacentrum.perun.audit.events.MailManagerEvents.MailSending;
3445
import cz.metacentrum.perun.audit.events.MailManagerEvents.MailSentForApplication;
35-
import cz.metacentrum.perun.core.api.*;
36-
import cz.metacentrum.perun.core.api.exceptions.*;
46+
import cz.metacentrum.perun.core.api.Attribute;
47+
import cz.metacentrum.perun.core.api.AuthzResolver;
48+
import cz.metacentrum.perun.core.api.BeansUtils;
49+
import cz.metacentrum.perun.core.api.ExtSourcesManager;
50+
import cz.metacentrum.perun.core.api.Group;
51+
import cz.metacentrum.perun.core.api.Member;
52+
import cz.metacentrum.perun.core.api.PerunBean;
53+
import cz.metacentrum.perun.core.api.PerunClient;
54+
import cz.metacentrum.perun.core.api.PerunPrincipal;
55+
import cz.metacentrum.perun.core.api.PerunSession;
56+
import cz.metacentrum.perun.core.api.RichUser;
57+
import cz.metacentrum.perun.core.api.Role;
58+
import cz.metacentrum.perun.core.api.RoleManagementRules;
59+
import cz.metacentrum.perun.core.api.User;
60+
import cz.metacentrum.perun.core.api.Vo;
61+
import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException;
62+
import cz.metacentrum.perun.core.api.exceptions.ConsistencyErrorException;
63+
import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException;
64+
import cz.metacentrum.perun.core.api.exceptions.InternalErrorException;
65+
import cz.metacentrum.perun.core.api.exceptions.InvalidHtmlInputException;
66+
import cz.metacentrum.perun.core.api.exceptions.PerunException;
67+
import cz.metacentrum.perun.core.api.exceptions.PrivilegeException;
68+
import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException;
69+
import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException;
70+
import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException;
3771
import cz.metacentrum.perun.core.bl.AttributesManagerBl;
3872
import cz.metacentrum.perun.core.bl.GroupsManagerBl;
3973
import cz.metacentrum.perun.core.bl.MembersManagerBl;
@@ -56,6 +90,7 @@
5690
import org.springframework.mail.MailException;
5791
import org.springframework.mail.javamail.JavaMailSender;
5892
import org.springframework.transaction.annotation.Transactional;
93+
import org.springframework.util.StringUtils;
5994

6095
import cz.metacentrum.perun.core.bl.PerunBl;
6196
import org.springframework.jdbc.core.JdbcPerunTemplate;
@@ -71,10 +106,20 @@
71106
import cz.metacentrum.perun.registrar.MailManager;
72107
import cz.metacentrum.perun.registrar.RegistrarManager;
73108

74-
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.*;
75109
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_FROM_EMAIL;
76110
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_FROM_NAME_EMAIL;
111+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_HTML_MAIL_FOOTER;
112+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_LANGUAGE_EMAIL;
113+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_MAIL_FOOTER;
114+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_REGISTRAR_URL;
115+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_TO_EMAIL;
77116
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_FROM_EMAIL;
117+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_FROM_NAME_EMAIL;
118+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_HTML_MAIL_FOOTER;
119+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_LANGUAGE_EMAIL;
120+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_MAIL_FOOTER;
121+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_REGISTRAR_URL;
122+
import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_TO_EMAIL;
78123

79124
public class MailManagerImpl implements MailManager {
80125

@@ -524,22 +569,16 @@ public void sendMessage(Application app, MailType mailType, String reason, List<
524569
// different behavior based on mail type
525570
switch (mail.getMailType()) {
526571
case APP_CREATED_USER:
527-
sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_CREATED_USER);
528-
break;
529572
case APPROVABLE_GROUP_APP_USER:
530-
sendUserMessage(app, mail, data, reason, exceptions, MailType.APPROVABLE_GROUP_APP_USER);
531-
break;
532-
case APP_CREATED_VO_ADMIN:
533-
appCreatedVoAdmin(app, mail, data, reason, exceptions);
573+
case APP_APPROVED_USER:
574+
case APP_REJECTED_USER:
575+
sendUserMessage(app, mail, data, reason, exceptions, mail.getMailType());
534576
break;
535577
case MAIL_VALIDATION:
536578
mailValidation(app, mail, data, reason, exceptions);
537579
break;
538-
case APP_APPROVED_USER:
539-
sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_APPROVED_USER);
540-
break;
541-
case APP_REJECTED_USER:
542-
sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_REJECTED_USER);
580+
case APP_CREATED_VO_ADMIN:
581+
appCreatedVoAdmin(app, mail, data, reason, exceptions);
543582
break;
544583
case APP_ERROR_VO_ADMIN:
545584
appErrorVoAdmin(app, mail, data, reason, exceptions);
@@ -1110,69 +1149,57 @@ private void setFromAndReplyTo(MimeMessage message, String fromName, String from
11101149
/**
11111150
* Get proper values "TO" for mail message based on VO or GROUP attribute "toEmail".
11121151
* <p>
1113-
* If group attribute not set and is group application, notify all corresponding group admins (admin roles configured to receive GROUP notifications).
1114-
* In case no such admins not set, use VO attribute.
1115-
* If Vo attribute not set (both group or vo application), notify all corresponding vo admins (admin roles configured to receive VO notifications).
1116-
* Otherwise, BACKUP_FROM address will be used.
1152+
* If group attribute is not set and Application is a group application, notify all corresponding group admins
1153+
* (admin roles configured to receive GROUP notifications).
1154+
* In case no such admins are set, try the same procedure on parent group (if exists)
1155+
* When reaches the top-level group and no recipients have been identified, repeat the same procedure on VO level (without cascading).
1156+
* When no recipient is identified on the VO level or some error happens, BACKUP_TO address will be used.
11171157
*
11181158
* @param app application to decide if it's VO or Group application
1119-
* @return list of mail addresses to send mail to
1159+
* @return Set of mail addresses to send mail to
11201160
*/
1121-
private List<String> getToMailAddresses(Application app) {
1122-
List<String> result = new ArrayList<>();
1123-
1124-
// get proper value from attribute
1161+
private Set<String> getToMailAddresses(Application app) {
1162+
Set<String> result = new HashSet<>();
11251163
try {
1126-
Attribute attrSenderEmail;
1127-
1128-
if (app.getGroup() == null) {
1129-
// it is a VO application
1130-
return getMailsFromVo(app, URN_VO_TO_EMAIL);
1131-
}
1132-
1133-
attrSenderEmail = attrManager.getAttribute(registrarSession, app.getGroup(), URN_GROUP_TO_EMAIL);
1134-
1135-
if (attrSenderEmail == null || attrSenderEmail.getValue() == null) {
1136-
// not specified toEmail group attribute
1137-
1138-
// try to use all group admins with specified preferred emails
1139-
List<String> rolesToNotify = getNotificationReceiverRoles(GROUP);
1140-
List<RichUser> admins = new ArrayList<>();
1141-
for (String role : rolesToNotify) {
1142-
admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, app.getGroup(), role));
1143-
}
1144-
result.addAll(getUserPreferredMails(registrarSession, admins));
1145-
result = result.stream().distinct().toList();
1146-
1164+
// Check group and its parent group hierarchy first
1165+
Group currentGroup = app.getGroup();
1166+
while (currentGroup != null) {
1167+
result = getToEmailsGroupLevel(currentGroup);
11471168
if (!result.isEmpty()) {
1148-
return result;
1169+
break;
11491170
}
1150-
return getMailsFromVo(app, URN_VO_TO_EMAIL);
1171+
currentGroup = getParentGroupForMailAddresses(currentGroup);
11511172
}
1152-
1153-
// specified toEmail group attribute, use valid emails
1154-
ArrayList<String> value = attrSenderEmail.valueAsList();
1155-
for (String adr : value) {
1156-
if (adr != null && !adr.isEmpty()) {
1157-
result.add(adr);
1158-
}
1173+
// VO application or fallback to VO level
1174+
if (app.getGroup() == null || result.isEmpty()) {
1175+
result = getToEmailsVoLevel(app.getVo());
11591176
}
1160-
11611177
} catch (Exception ex) {
11621178
// we don't care about exceptions here - we have backup TO/FROM address
1163-
if (app.getGroup() == null) {
1164-
log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}", URN_VO_TO_EMAIL, ex);
1165-
} else {
1166-
log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}", URN_GROUP_TO_EMAIL, ex);
1167-
}
1179+
log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}",
1180+
app.getGroup() == null ? URN_VO_TO_EMAIL : URN_GROUP_TO_EMAIL, ex);
11681181
// set backup
11691182
result.clear();
1183+
}
1184+
// no recipients found for Group and Vo, use backup
1185+
if (result.isEmpty()) {
11701186
result.add(getPropertyFromConfiguration("backupTo"));
11711187
}
1172-
11731188
return result;
11741189
}
11751190

1191+
private Group getParentGroupForMailAddresses(Group group) {
1192+
if (group != null && group.getParentGroupId() != null) {
1193+
try {
1194+
return groupsManager.getGroupById(registrarSession, group.getParentGroupId());
1195+
} catch (GroupNotExistsException e) {
1196+
log.error("[MAIL MANAGER] Inconsistency detected - parent (in hierarchy) group with ID '{}' of group '{}' does not exist",
1197+
group.getParentGroupId(), group);
1198+
}
1199+
}
1200+
return null;
1201+
}
1202+
11761203
/**
11771204
* Returns list of roles, which should be notified for object's notification type
11781205
* @return list of role names
@@ -2006,7 +2033,7 @@ private void appCreatedVoAdmin(Application app, ApplicationMail mail, List<Appli
20062033
MimeMessage message = getAdminMessage(app, mail, data, reason, exceptions);
20072034

20082035
// send a message to all VO or Group admins
2009-
List<String> toEmail = getToMailAddresses(app);
2036+
Set<String> toEmail = getToMailAddresses(app);
20102037
for (String email : toEmail) {
20112038
setRecipient(message, email);
20122039
try {
@@ -2163,7 +2190,7 @@ private void appErrorVoAdmin(Application app, ApplicationMail mail, List<Applica
21632190
MimeMessage message = getAdminMessage(app, mail, data, reason, exceptions);
21642191

21652192
// send a message to all VO or Group admins
2166-
List<String> toEmail = getToMailAddresses(app);
2193+
Set<String> toEmail = getToMailAddresses(app);
21672194

21682195
for (String email : toEmail) {
21692196
setRecipient(message, email);
@@ -2557,45 +2584,90 @@ private void setHtmlMessageWithAltPlainTextMessage (MimeMessage message, String
25572584
}
25582585

25592586
/**
2560-
* Get notification emails for given VO. If toEmail is not specified,
2587+
* Get notification emails for given VO. If toEmail (URN_VO_TO_EMAIL) has no value set,
25612588
* all configured roles for receiving VO notifications are used.
25622589
*
2563-
* @param app Application to get VO from
2564-
* @param voEmailAttr String name of given email attribute
2565-
* @return list of emails used for sending notifications.
2590+
* @param vo Virtual Organization for which the lookup is performed. If null, empty result will be returned.
2591+
* @return List of emails used for sending notifications (can be empty).
25662592
*/
2567-
private List<String> getMailsFromVo(Application app, String voEmailAttr)
2568-
throws AttributeNotExistsException, WrongAttributeAssignmentException {
2569-
2570-
List<String> emails = new ArrayList<>();
2593+
private Set<String> getToEmailsVoLevel(Vo vo)
2594+
throws AttributeNotExistsException, WrongAttributeAssignmentException
2595+
{
2596+
if (vo == null) {
2597+
return new HashSet<>();
2598+
}
2599+
Attribute toEmailAttr = attrManager.getAttribute(registrarSession, vo, URN_VO_TO_EMAIL);
2600+
Set<String> emails = getToEmailsFromAttribute(toEmailAttr);
2601+
if (emails.isEmpty()) {
2602+
emails = getToEmailsViaRoles(VO, vo);
2603+
}
2604+
return emails;
2605+
}
25712606

2572-
Attribute attrToEmail = attrManager.getAttribute(registrarSession, app.getVo(), voEmailAttr);
2607+
/**
2608+
* Get notification emails for given Group. If toEmail (URN_GROUP_TO_EMAIL) has no value set,
2609+
* all configured roles for receiving GROUP notifications are used.
2610+
*
2611+
* @param group Group for which the lookup is performed. If null, empty result will be returned.
2612+
* @return List of emails used for sending notifications (can be empty).
2613+
*/
2614+
private Set<String> getToEmailsGroupLevel(Group group)
2615+
throws AttributeNotExistsException, WrongAttributeAssignmentException
2616+
{
2617+
if (group == null) {
2618+
return new HashSet<>();
2619+
}
2620+
Attribute toEmailAttr = attrManager.getAttribute(registrarSession, group, URN_GROUP_TO_EMAIL);
2621+
Set<String> emails = getToEmailsFromAttribute(toEmailAttr);
2622+
if (emails.isEmpty()) {
2623+
emails = getToEmailsViaRoles(GROUP, group);
2624+
}
2625+
return emails;
2626+
}
25732627

2628+
/**
2629+
* Get "TO" email addresses of notification recipient(s) from the attribute
2630+
* @param toEmailAttr Attribute with the recipients as value
2631+
* @return Set of identified emails. Can be empty if attr has no non-blank item in array value or no value at all.
2632+
*/
2633+
private Set<String> getToEmailsFromAttribute(Attribute toEmailAttr) {
25742634
// if there are any pre-defined VO emailTo attributes, use them
2575-
if (attrToEmail != null && attrToEmail.getValue() != null) {
2576-
ArrayList<String> value = attrToEmail.valueAsList();
2635+
Set<String> emails = new HashSet<>();
2636+
if (toEmailAttr != null && toEmailAttr.getValue() != null) {
2637+
ArrayList<String> value = toEmailAttr.valueAsList();
25772638
for (String adr : value) {
2578-
if (adr != null && !adr.isEmpty()) {
2639+
if (StringUtils.hasText(adr)) {
25792640
emails.add(adr);
25802641
}
25812642
}
2582-
return emails;
25832643
}
2644+
return emails;
2645+
}
25842646

2585-
// in case no pre-defined attribute was found, use all configured roles with specified preferred email
2647+
/**
2648+
* Get "TO" emails from notification receiver roles of the specified object.
2649+
* @param object String specification of the object
2650+
* @param objectInstance instance of the object
2651+
* @return Set of identified emails. Can be empty if not roles or users in the role have been identified,
2652+
* or they do not have email address set.
2653+
* @throws WrongAttributeAssignmentException
2654+
* @throws AttributeNotExistsException
2655+
*/
2656+
private Set<String> getToEmailsViaRoles(String object, PerunBean objectInstance)
2657+
throws WrongAttributeAssignmentException, AttributeNotExistsException
2658+
{
2659+
Set<String> emails = new HashSet<>();
25862660
try {
2587-
List<String> rolesToNotify = getNotificationReceiverRoles(VO);
2661+
List<String> rolesToNotify = getNotificationReceiverRoles(object);
25882662
List<RichUser> admins = new ArrayList<>();
25892663
for (String role : rolesToNotify) {
2590-
admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, app.getVo(), role));
2664+
admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, objectInstance, role));
25912665
}
25922666
emails.addAll(getUserPreferredMails(registrarSession, admins));
2593-
emails = emails.stream().distinct().toList();
2667+
return emails;
25942668
} catch (RoleCannotBeManagedException e) {
25952669
throw new InternalErrorException(e);
25962670
}
2597-
2598-
return emails;
25992671
}
26002672

26012673
/**

0 commit comments

Comments
 (0)