From bb463bbdaf7239b46ef532c034df47b05d2ff6cf Mon Sep 17 00:00:00 2001 From: David Flor <493294@mail.muni.cz> Date: Mon, 16 Jan 2023 13:27:59 +0100 Subject: [PATCH 01/28] fix(registrar): fix policies for enabling/disabling group notifications * the vo/group policies for setSendingEnabled were swapped, fixed it --- perun-base/src/main/resources/perun-roles.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 15e6ee3f3e..620726265c 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -7225,20 +7225,20 @@ perun_policies: vo-setSendingEnabled_ApplicationMail_policy: policy_roles: - VOADMIN: Vo - - GROUPADMIN: Group include_policies: - default_policy mfa_rules: - MFA: Vo - - MFA: Group group-setSendingEnabled_ApplicationMail_policy: policy_roles: - VOADMIN: Vo + - GROUPADMIN: Group include_policies: - default_policy mfa_rules: - MFA: Vo + - MFA: Group source-copyMailsFromVoToVo_Vo_Vo_policy: policy_roles: From 853edd3ecac61f052c1339e0ca179d2b389fcf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Thu, 19 Jan 2023 08:38:08 +0100 Subject: [PATCH 02/28] fix(core): load all roles for service principal if not part of dontlookup config * now service principals got their roles based on configuration in coreConfig and further roles were not assigned * if service principal is missing in the dontlookupusers configuration, we should load also other roles for him --- .../cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 9fde39d38d..2b528d9872 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -2499,8 +2499,8 @@ public static synchronized void refreshAuthz(PerunSession sess) { //Prepare service roles like engine, service, registrar, perunAdmin etc. boolean serviceRole = prepareServiceRoles(sess); - // if have some of the service principal, we do not need to search further - if (!serviceRole) { + // no need to search further for service principals included in 'dontlookupusers' configuration + if (!serviceRole || !BeansUtils.getCoreConfig().getDontLookupUsers().contains(sess.getPerunPrincipal().getActor())) { User user = sess.getPerunPrincipal().getUser(); AuthzRoles roles; if (user == null) { From 7dca456c35ebb3bc760b61c800344d97a21a0039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Wed, 11 Jan 2023 11:08:28 +0100 Subject: [PATCH 03/28] fix(core): synchronization updates group name * groups are matched by group login, so group name can and should be updated by synchronization --- .../core/blImpl/GroupsManagerBlImpl.java | 65 ++++++++++--------- ...ructureSynchronizationIntegrationTest.java | 12 ++-- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/GroupsManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/GroupsManagerBlImpl.java index cf90e3ab3e..59e8f6389c 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/GroupsManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/GroupsManagerBlImpl.java @@ -4583,8 +4583,6 @@ private List> getSubjectGroupsFromExtSource(PerunSession ses /** * Get Map groupsToUpdate and update their parent group and description. - * We don't have to update short name, because if short name has changed, group will be removed and created with new name. - * * If some problem occurs, add groupToUpdate to skippedGroups and skip it. * * Method is used by group structure synchronization. @@ -4629,12 +4627,7 @@ private void updateExistingGroupsWhileSynchronization(PerunSession sess, Group b } } - boolean changed = updateGroupDescription(sess, groupToUpdate, candidateGroup); - if(changed) { - log.trace("Group structure synchronization {}: value of the group description for groupId {} changed. Original value {}, new value {}.", - baseGroup, groupToUpdate.getId(), groupToUpdate.getDescription(), candidateGroup.asGroup().getDescription()); - } - + updateGroupDetails(sess, baseGroup, groupToUpdate, candidateGroup); } } @@ -4742,40 +4735,50 @@ private String getOldParentGroupLogin(PerunSession sess, Group groupToUpdate, Gr } /** - * Update group description. + * Update group name and description. * * Method is used by group structure synchronization. * * @param sess - * @param groupWithOldDescription group with description which will be chaged - * @param groupWithNewDescription candidate group with description which will replace the old one - * @return true if description changed, false otherwise + * @param baseGroup base group (for logging purposes) + * @param oldGroup group which will be changed + * @param updatedGroup candidate group with details that will replace the old one + * @return true if description or name changed, false otherwise * @throws InternalErrorException */ - private boolean updateGroupDescription(PerunSession sess, Group groupWithOldDescription, CandidateGroup groupWithNewDescription) { - //If the old description is not null, compare it with the newDescription and update it if they differ - if(groupWithOldDescription.getDescription() != null) { - if(!groupWithOldDescription.getDescription().equals(groupWithNewDescription.asGroup().getDescription())){ - groupWithOldDescription.setDescription(groupWithNewDescription.asGroup().getDescription()); - try { - groupsManagerImpl.updateGroup(sess, groupWithOldDescription); - } catch (GroupExistsException ex) { - throw new InternalErrorException("Unexpected exception when trying to modify group description!"); - } - getPerunBl().getAuditer().log(sess, new GroupUpdated(groupWithOldDescription)); - return true; - } - // If the new description is not null set the old description to new one - } else if(groupWithNewDescription.asGroup().getDescription() != null){ - groupWithOldDescription.setDescription(groupWithNewDescription.asGroup().getDescription()); + private boolean updateGroupDetails(PerunSession sess, Group baseGroup, Group oldGroup, CandidateGroup updatedGroup) { + Group oldGroupCopy = new Group(oldGroup.getName(), oldGroup.getDescription()); + oldGroupCopy.setShortName(oldGroup.getShortName()); + + boolean updatedDescription = !Objects.equals(oldGroup.getDescription(), updatedGroup.asGroup().getDescription()); + if (updatedDescription) { + oldGroup.setDescription(updatedGroup.asGroup().getDescription()); + } + + boolean updatedShortname = !Objects.equals(oldGroup.getShortName(), updatedGroup.asGroup().getShortName()); + if (updatedShortname) { + oldGroup.setShortName(updatedGroup.asGroup().getShortName()); + } + + if (updatedDescription || updatedShortname) { try { - groupsManagerImpl.updateGroup(sess, groupWithOldDescription); + groupsManagerImpl.updateGroup(sess, oldGroup); } catch (GroupExistsException ex) { - throw new InternalErrorException("Unexpected exception when trying to modify group description!"); + throw new InternalErrorException("Unexpected exception when trying to modify group details", ex); } - getPerunBl().getAuditer().log(sess, new GroupUpdated(groupWithOldDescription)); + getPerunBl().getAuditer().log(sess, new GroupUpdated(oldGroup)); + if (updatedDescription) { + log.trace("Group structure synchronization {}: value of the group description for groupId {} changed. Original value {}, new value {}.", + baseGroup, oldGroup.getId(), oldGroupCopy.getDescription(), oldGroup.getDescription()); + } + if (updatedShortname) { + log.trace("Group structure synchronization {}: shortName of the group for groupId {} changed. Original value {}, new value {}.", + baseGroup, oldGroup.getId(), oldGroupCopy.getShortName(), oldGroup.getShortName()); + } + return true; } + return false; } diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/GroupAndGroupStructureSynchronizationIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/GroupAndGroupStructureSynchronizationIntegrationTest.java index b6a37d1cfa..1a12a03086 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/GroupAndGroupStructureSynchronizationIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/GroupAndGroupStructureSynchronizationIntegrationTest.java @@ -418,14 +418,14 @@ public void replaceStructureWithPrefix() throws Exception { } @Test - public void modifyGroupNameTest() throws Exception { - System.out.println(CLASS_NAME + "modifyGroupNameTest"); + public void modifyGroupLoginTest() throws Exception { + System.out.println(CLASS_NAME + "modifyGroupLoginTest"); final Group subBaseGroup = new Group("group1", "child of base group"); groupsManagerBl.createGroup(sess, baseGroup, subBaseGroup); setLoginToGroup(baseGroup, subBaseGroup, "group1"); - final TestGroup modifiedSubBaseTestGroup = new TestGroup("modified", "modified", null, "child of base group"); + final TestGroup modifiedSubBaseTestGroup = new TestGroup("group1", "modified", null, "child of base group"); List> subjects = Collections.singletonList(modifiedSubBaseTestGroup.toMap()); when(essa.getSubjectGroups(anyMap())).thenReturn(subjects); @@ -440,14 +440,14 @@ public void modifyGroupNameTest() throws Exception { } @Test - public void modifyGroupDescriptionTest() throws Exception { - System.out.println(CLASS_NAME + "modifyGroupDescriptionTest"); + public void modifyGroupDetailsTest() throws Exception { + System.out.println(CLASS_NAME + "modifyGroupDetailsTest"); final Group subBaseGroup = new Group("group1", "child of base group"); groupsManagerBl.createGroup(sess, baseGroup, subBaseGroup); setLoginToGroup(baseGroup, subBaseGroup, "group1"); - final TestGroup modifiedSubBaseTestGroup = new TestGroup("group1", "group1", null, "modified"); + final TestGroup modifiedSubBaseTestGroup = new TestGroup("modifiedName", "group1", null, "modifiedDescription"); List> subjects = Collections.singletonList(modifiedSubBaseTestGroup.toMap()); when(essa.getSubjectGroups(anyMap())).thenReturn(subjects); From 6267b11d30e7fc5cadac30df2c39e8676af5abcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:34:28 +0000 Subject: [PATCH 04/28] fix(deps): update dependency org.springframework.boot:spring-boot-starter-parent to v2.7.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7730cc5a70..aab32deda7 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.7 + 2.7.8 From 70c197d77b0dde2a850582b78058fca748d42a75 Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Mon, 19 Dec 2022 13:05:53 +0100 Subject: [PATCH 05/28] feat(core): member candidates methods - addMemberCandidates - inviteMemberCandidates - invitationFormExists --- perun-base/src/main/resources/perun-roles.yml | 20 +++++ .../perun/core/api/MembersManager.java | 41 ++++++++++ .../perun/core/entry/MembersManagerEntry.java | 44 ++++++++++ .../MembersManagerEntryIntegrationTest.java | 80 +++++++++++++++++- perun-openapi/openapi.yml | 81 +++++++++++++++++++ .../perun/registrar/MailManager.java | 10 +++ .../perun/registrar/RegistrarManager.java | 11 +++ .../perun/registrar/impl/MailManagerImpl.java | 19 +++++ .../registrar/impl/RegistrarManagerImpl.java | 28 +++++++ .../RegistrarBaseIntegrationTest.java | 26 ++++++ .../rpc/methods/MembersManagerMethod.java | 34 ++++++++ .../rpc/methods/RegistrarManagerMethod.java | 48 +++++++++++ 12 files changed, 441 insertions(+), 1 deletion(-) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 620726265c..6464fd8c92 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -1501,6 +1501,26 @@ perun_policies: - MFA: Group - MFA: Vo + addMemberCandidates_Vo_List_Group_policy: + policy_roles: + - GROUPADMIN: Group + - GROUPMEMBERSHIPMANAGER: Group + - SPREGAPPLICATION: + - VOADMIN: Vo + include_policies: + - default_policy + mfa_rules: + - MFA: Group + - MFA: Vo + + addMemberCandidates_Vo_List_policy: + policy_roles: + - VOADMIN: Vo + include_policies: + - default_policy + mfa_rules: + - MFA: Vo + removeMember_Group_Member_policy: policy_roles: - GROUPADMIN: Group diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java index 95d7e09607..c4beee623f 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java @@ -6,6 +6,7 @@ import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.GroupResourceMismatchException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; @@ -30,6 +31,7 @@ import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.BanAlreadyExistsException; @@ -1538,4 +1540,43 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @return List RichMembers with specified IDs and attributes */ List getRichMembersByIds(PerunSession sess, List ids, List attrsNames) throws PrivilegeException, AttributeNotExistsException; + + /** + * Add member candidates. + * + * @param sess Perun session + * @param vo vo + * @param candidates List list of member candidates + * @throws PrivilegeException insufficient permissions + * @throws GroupNotExistsException group does not exist + * @throws UserNotExistsException user does not exist + * @throws WrongAttributeValueException attribute value is illegal + * @throws AlreadyMemberException candidate is already member + * @throws WrongReferenceAttributeValueException attribute value is illegal + * @throws ExtendMembershipException reason why user can't extend membership + * @throws VoNotExistsException vo does not exist + */ + void addMemberCandidates(PerunSession sess, Vo vo, List candidates) throws PrivilegeException, GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException; + + /** + * Add member candidates to Group. + * + * @param sess Perun session + * @param vo vo + * @param candidates List list of member candidates + * @param group group + * @throws PrivilegeException insufficient permissions + * @throws ExternallyManagedException group is externally managed + * @throws MemberNotExistsException member does not exist + * @throws GroupNotExistsException group does not exist + * @throws WrongReferenceAttributeValueException attribute value is illegal + * @throws WrongAttributeAssignmentException attribute does not belong to appropriate entity + * @throws AttributeNotExistsException attribute does not exist + * @throws AlreadyMemberException candidate is already member + * @throws WrongAttributeValueException attribute value is illegal + * @throws UserNotExistsException user does not exist + * @throws ExtendMembershipException reason why user can't extend membership + * @throws VoNotExistsException vo does not exist + */ + void addMemberCandidates(PerunSession sess, Vo vo, List candidates, Group group) throws PrivilegeException, ExternallyManagedException, MemberNotExistsException, GroupNotExistsException, WrongReferenceAttributeValueException, WrongAttributeAssignmentException, AttributeNotExistsException, AlreadyMemberException, WrongAttributeValueException, UserNotExistsException, ExtendMembershipException, VoNotExistsException; } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index 5024e8d11d..bdff373ef8 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -4,6 +4,7 @@ import cz.metacentrum.perun.core.api.AttributeDefinition; import cz.metacentrum.perun.core.api.AuthzResolver; import cz.metacentrum.perun.core.api.Candidate; +import cz.metacentrum.perun.core.api.MemberCandidate; import cz.metacentrum.perun.core.api.NamespaceRules; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.MembersPageQuery; @@ -29,6 +30,7 @@ import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.GroupResourceMismatchException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; @@ -1729,6 +1731,48 @@ public List getRichMembersByIds(PerunSession sess, List ids return membersManagerBl.filterOnlyAllowedAttributes(sess, richMembers); } + @Override + public void addMemberCandidates(PerunSession sess, Vo vo, List candidates) throws PrivilegeException, GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException { + Utils.checkPerunSession(sess); + + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "addMemberCandidates_Vo_List_policy", vo)) { + throw new PrivilegeException(sess, "addMemberCandidates"); + } + + for (MemberCandidate candidate : candidates) { + if (candidate.getRichUser() != null) { + Member member = this.createMember(sess, vo, candidate.getRichUser()); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } else if (candidate.getCandidate() != null) { + Member member = this.createMember(sess, vo, candidate.getCandidate()); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } + } + } + + @Override + public void addMemberCandidates(PerunSession sess, Vo vo, List candidates, Group group) throws PrivilegeException, ExternallyManagedException, MemberNotExistsException, GroupNotExistsException, WrongReferenceAttributeValueException, WrongAttributeAssignmentException, AttributeNotExistsException, AlreadyMemberException, WrongAttributeValueException, UserNotExistsException, ExtendMembershipException, VoNotExistsException { + Utils.checkPerunSession(sess); + + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "addMemberCandidates_Vo_List_Group_policy", group)) { + throw new PrivilegeException(sess, "addMemberCandidates"); + } + + for (MemberCandidate candidate : candidates) { + if (candidate.getMember() != null) { + getPerunBl().getGroupsManager().addMember(sess, group, candidate.getMember()); + } else if (candidate.getRichUser() != null) { + Member member = this.createMember(sess, vo, candidate.getRichUser(), List.of(group)); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } else if (candidate.getCandidate() != null) { + Member member = this.createMember(sess, vo, candidate.getCandidate(), List.of(group)); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } + } + } + /** * Converts member to member with sponsors and sets all his sponsors. * diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java index dd9a03bb47..c44a0e8e7a 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java @@ -14,12 +14,14 @@ import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.GroupsManager; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.MemberCandidate; import cz.metacentrum.perun.core.api.MemberGroupStatus; import cz.metacentrum.perun.core.api.MemberWithSponsors; import cz.metacentrum.perun.core.api.MembersManager; import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.NamespaceRules; +import cz.metacentrum.perun.core.api.RichUser; import cz.metacentrum.perun.core.api.SortingOrder; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.PerunBean; @@ -42,15 +44,19 @@ import cz.metacentrum.perun.core.api.exceptions.AlreadySponsorException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsoredMemberException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.api.exceptions.MemberLifecycleAlteringForbiddenException; import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParseUserNameException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.modules.attributes.AbstractMembershipExpirationRulesModule; import cz.metacentrum.perun.core.api.SponsoredUserData; @@ -70,7 +76,6 @@ import java.util.Map; import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS; -import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS_HISTORY; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.VO_EXPIRATION_RULES_ATTR; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.expireSponsoredMembers; import static java.util.stream.Collectors.toList; @@ -250,6 +255,34 @@ public void getAllSponsoredMembersAndTheirSponsors() throws Exception { assertEquals(1, memberWithSponsors.get(0).getSponsors().size()); } + @Test + public void addMemberCandidates() throws Exception { + System.out.println(CLASS_NAME + "addMemberCandidates"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(123, "test", "test")); + + MemberCandidate memberCandidate1 = getMemberCandidateWithCandidate(); + MemberCandidate memberCandidate2 = getMemberCandidateWithRichUser(); + + perun.getMembersManager().addMemberCandidates(sess, vo, List.of(memberCandidate1, memberCandidate2)); + assertEquals(2, perun.getMembersManager().getMembers(sess, vo).size()); + } + + @Test + public void addMemberCandidatesGroup() throws Exception { + System.out.println(CLASS_NAME + "addMemberCandidatesGroup"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(123, "test", "test")); + Group group = perun.getGroupsManager().createGroup(sess, vo, new Group("test", "test")); + + MemberCandidate memberCandidate1 = getMemberCandidateWithCandidate(); + MemberCandidate memberCandidate2 = getMemberCandidateWithRichUser(); + MemberCandidate memberCandidate3 = getMemberCandidateWithMember(vo); + + perun.getMembersManager().addMemberCandidates(sess, vo, List.of(memberCandidate1, memberCandidate2, memberCandidate3), group); + assertEquals(3, perun.getMembersManager().getMembers(sess, vo).size()); + } + @Test public void createMember() throws Exception { System.out.println(CLASS_NAME + "createMemberSync"); @@ -3605,4 +3638,49 @@ private Member createSponsoredMember(PerunSession sess, Vo vo, String namespace, return perun.getMembersManagerBl().createSponsoredMember(sess, input, vo, sponsor, null, false, null,null, Validation.SYNC); } + + private MemberCandidate getMemberCandidateWithCandidate() { + Candidate candidate = new Candidate(); + candidate.setFirstName("Test"); + candidate.setId(0); + candidate.setMiddleName(""); + candidate.setLastName("Test"); + candidate.setTitleBefore(""); + candidate.setTitleAfter(""); + candidate.setUserExtSource(new UserExtSource(new ExtSource(0, "testExtSource", ExtSourcesManager.EXTSOURCE_INTERNAL), "Test1")); + candidate.setAttributes(new HashMap<>()); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setCandidate(candidate); + + return memberCandidate; + } + + private MemberCandidate getMemberCandidateWithRichUser() { + User user = new User(); + user.setFirstName("Test2"); + user.setLastName("Test2"); + user = perun.getUsersManagerBl().createUser(sess, user); + + RichUser richUser = new RichUser(user, List.of(new UserExtSource(new ExtSource(1, "test2ExtSource", ExtSourcesManager.EXTSOURCE_INTERNAL), "Test2"))); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setRichUser(richUser); + + return memberCandidate; + } + + private MemberCandidate getMemberCandidateWithMember(Vo vo) throws GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, PrivilegeException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException { + User userForMember = new User(); + userForMember.setFirstName("Test3"); + userForMember.setLastName("Test3"); + userForMember = perun.getUsersManagerBl().createUser(sess, userForMember); + + Member member = perun.getMembersManager().createMember(sess, vo, userForMember); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setMember(member); + + return memberCandidate; + } } diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index a7d461aaf4..0d7c6db7b2 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -3475,6 +3475,16 @@ components: in: query required: false + memberCandidates: + name: candidates[] + description: list of member candidates + schema: + type: array + items: + $ref: "#/components/schemas/MemberCandidate" + in: query + required: true + ################################################# @@ -10919,6 +10929,33 @@ paths: attrNames: { type: array, items: { type: string } } query: { $ref: '#/components/schemas/MembersPageQuery' } + /json/membersManager/addMemberCandidates: + post: + tags: + - membersManager + operationId: addMemberCandidates + summary: Adds member candidates. + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + requestBody: + required: true + content: + application/json: + schema: + title: InputAddMemberCandidates + description: "input to add member candidates" + type: object + required: + - vo + - candidates + properties: + vo: { type: integer } + candidates: { type: array, items: { $ref: '#/components/schemas/MemberCandidate' } } + group: { type: integer } + ################################################# # # # FacilitiesManager # @@ -16764,6 +16801,50 @@ paths: default: $ref: '#/components/responses/ExceptionResponse' + /json/registrarManager/inviteMemberCandidates: + post: + tags: + - registrarManager + operationId: inviteMemberCandidates + summary: Invite member candidates. + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + requestBody: + required: true + content: + application/json: + schema: + title: InputInviteMemberCandidates + description: "input to invite member candidates" + type: object + required: + - vo + - candidates + - lang + properties: + vo: { type: integer } + candidates: { type: array, items: { $ref: '#/components/schemas/MemberCandidate' } } + lang: { type: string } + group: { type: integer } + + /json/registrarManager/invitationFormExists: + get: + tags: + - registrarManager + operationId: invitationFormExists + summary: Checks if invitation form exists. + parameters: + - $ref: '#/components/parameters/voId' + - $ref: '#/components/parameters/groupId' + responses: + '200': + $ref: '#/components/responses/BooleanResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + ################################################# # # # ServicesManager # diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java index 9888083a72..37c6e5e232 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java @@ -227,4 +227,14 @@ public interface MailManager { */ String getPropertyFromConfiguration(String input); + /** + * Checks if invitation form exists + * + * @param vo vo + * @param group group + * @return true if invitation form exists, false otherwise + * @throws VoNotExistsException when vo does not exist + * @throws GroupNotExistsException when group is defined and does not exist + */ + Boolean invitationFormExists(PerunSession sess, Vo vo, Group group) throws VoNotExistsException, GroupNotExistsException; } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java index 8d4908ed16..435f7917b8 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java @@ -662,4 +662,15 @@ public interface RegistrarManager { */ void addGroupsToAutoRegistration(PerunSession sess, List groups) throws GroupNotExistsException, PrivilegeException, GroupNotAllowedToAutoRegistrationException; + /** + * Invite member candidates. + * + * @param sess session + * @param vo Vo + * @param lang language + * @param candidates list of member candidates + * @param group group + * @throws PerunException unable to invite some candidate + */ + void inviteMemberCandidates(PerunSession sess, Vo vo, Group group, String lang, List candidates) throws PerunException; } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java index d3c2211f24..112f686ae4 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java @@ -794,6 +794,25 @@ public String getPropertyFromConfiguration(String propertyName) { return EMPTY_STRING; } + @Override + public Boolean invitationFormExists(PerunSession sess, Vo vo, Group group) throws VoNotExistsException, GroupNotExistsException { + Utils.checkPerunSession(sess); + + perun.getVosManagerBl().checkVoExists(sess, vo); + if (group != null) { + perun.getGroupsManagerBl().checkGroupExists(sess, group); + } + + try { + ApplicationForm form = getForm(vo, group); + getMail(form, AppType.INITIAL, MailType.USER_INVITE); + } catch (FormNotExistsException | RegistrarException e) { + return false; + } + + return true; + } + /** * Retrieve mail definition from db by params. * Mail contains all texts. diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java index 2347dfa287..efd147d93e 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java @@ -3597,6 +3597,19 @@ public void addGroupsToAutoRegistration(PerunSession sess, List groups) t perun.getGroupsManagerBl().addGroupsToAutoRegistration(sess, groups); } + @Override + public void inviteMemberCandidates(PerunSession sess, Vo vo, Group group, String lang, List candidates) throws PerunException { + Utils.checkPerunSession(sess); + + for (MemberCandidate candidate : candidates) { + if (candidate.getRichUser() != null) { + mailManager.sendInvitation(sess, vo, group, perun.getUsersManager().getUserById(sess, candidate.getRichUser().getId())); + } else if (candidate.getCandidate() != null) { + mailManager.sendInvitation(sess, vo, group, null, getCandidateEmail(candidate.getCandidate()), lang); + } + } + } + @Override public void handleUsersGroupApplications(PerunSession sess, Vo vo, User user) throws PerunException { // get group apps based on the vo @@ -3654,6 +3667,21 @@ public ConsolidatorManager getConsolidatorManager() { return this.consolidatorManager; } + /** + * Gets email for candidate. + * + * @param candidate candidate + * @return email + */ + private String getCandidateEmail(Candidate candidate) { + if (candidate.getAttributes().containsKey("urn:perun:member:attribute-def:def:mail")) { + return candidate.getAttributes().get("urn:perun:member:attribute-def:def:mail"); + } else if (candidate.getAttributes().containsKey("urn:perun:user:attribute-def:def:preferredMail")) { + return candidate.getAttributes().get("urn:perun:user:attribute-def:def:preferredMail"); + } + return ""; + } + /** * Set application to VERIFIED state if all it's * mails (VALIDATED_EMAIL) have assuranceLevel >= 1 and have non-empty value (there is anything to validate). diff --git a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java index 4aeac3db14..590a443e3b 100644 --- a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java +++ b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java @@ -1239,6 +1239,32 @@ public void getApplicationsPageApplicationFormSearch() throws Exception { assertThat(result.getData().get(0).getFormData().size()).isEqualTo(2); } + @Test + public void invitationFormExistsForGroup() throws Exception { + Group groupWithInvitation = perun.getGroupsManagerBl().createGroup(session, vo, new Group("group1", "group with form")); + Group groupWithoutInvitation = perun.getGroupsManagerBl().createGroup(session, vo, new Group("group2", "group without form")); + + registrarManager.createApplicationFormInGroup(session, groupWithInvitation); + ApplicationForm form = registrarManager.getFormForGroup(groupWithInvitation); + ApplicationMail mail = new ApplicationMail(0, INITIAL, form.getId(), MailType.USER_INVITE, true); + mailManager.addMail(session, form, mail); + + assertTrue(mailManager.invitationFormExists(session, vo, groupWithInvitation)); + assertFalse(mailManager.invitationFormExists(session, vo, groupWithoutInvitation)); + } + + @Test + public void invitationFormExistsForVo() throws Exception { + Vo voWithoutInvitation = perun.getVosManager().createVo(session, new Vo(1234, "test", "test")); + + ApplicationForm form = registrarManager.getFormForVo(vo); + ApplicationMail mail = new ApplicationMail(0, INITIAL, form.getId(), MailType.USER_INVITE, true); + mailManager.addMail(session, form, mail); + + assertTrue(mailManager.invitationFormExists(session, vo, null)); + assertFalse(mailManager.invitationFormExists(session, voWithoutInvitation, null)); + } + @Test public void getApplicationsPageMultipleFormItems() throws Exception { System.out.println("getApplicationsPageMultipleFormItems"); diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java index ba9b710cc8..a7fbc9bcb8 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java @@ -2037,5 +2037,39 @@ public Object call(ApiCaller ac, Deserializer parms) throws PerunException { parms.read("query", MembersPageQuery.class), parms.readList("attrNames", String.class)); } + }, + + /*# + * Add member candidates. + * + * @param vo int Vo id + * @param candidates List Member candidates + */ + /*# + * Add member candidates to group. + * + * @param vo int Vo id + * @param candidates List Member candidates + * @param group int Group id + */ + addMemberCandidates { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + if (parms.contains("group")) { + ac.getMembersManager().addMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.readList("candidates", MemberCandidate.class), + ac.getGroupById(parms.readInt("group")) + ); + } else { + ac.getMembersManager().addMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.readList("candidates", MemberCandidate.class) + ); + } + return null; + } } } diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java index a39d9c3973..435ceb0fde 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java @@ -138,6 +138,54 @@ public Void call(ApiCaller ac, Deserializer parms) throws PerunException { }, + /*# + * Invite member candidates. If candidate contains richUser, then his preferred mail is retrieved and used, otherwise email must be passed in candidate's attributes. + * + * @param vo Vo id + * @param lang language + * @param candidates List list of member candidates + */ + /*# + * Invite member candidates to group. If candidate contains richUser, then his preferred mail is retrieved and used, otherwise email must be passed in candidate's attributes. + * + * @param vo Vo id + * @param lang language + * @param candidates List list of member candidates + * @param group Group id + */ + inviteMemberCandidates { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + ac.getRegistrarManager().inviteMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.contains("group") ? ac.getGroupById(parms.readInt("group")) : null, + parms.readString("lang"), + parms.readList("candidates", MemberCandidate.class) + ); + return null; + } + }, + + /*# + * Checks if invitation form exists. + * + * @param vo Vo id + * @param group Group id + * + * @return true if invitation form exists, false otherwise + */ + invitationFormExists { + @Override + public Boolean call(ApiCaller ac, Deserializer parms) throws PerunException { + return ac.getRegistrarManager().getMailManager().invitationFormExists( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.contains("group") ? ac.getGroupById(parms.readInt("group")) : null + ); + } + }, + /*# * Send invitations with link to VO / Group application form from provided csv data * From 0fc8a493e1d0c00b8dd1122caa29bc94b144df3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Mon, 23 Jan 2023 13:00:27 +0100 Subject: [PATCH 06/28] docs: fixed docs of API methods for creation of sponsored members --- .../rpc/methods/MembersManagerMethod.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java index ba9b710cc8..1f6c4327aa 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java @@ -166,11 +166,11 @@ public Member call(ApiCaller ac, Deserializer parms) throws PerunException { * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. - * @param sendActivationLink (optional) boolean if true link for manual activation of account will be send to the email + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. + * @param sendActivationLink boolean (optional) If true link for manual activation of account will be sent to the email * default is false, can't be used with empty email parameter * If set to true, a non-empty namespace has to be provided. - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. * @return RichMember newly created sponsored member */ createSponsoredMember { @@ -275,7 +275,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param password String password * @param login String login * @param sponsor int id of sponsoring user - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return RichMember sponsored member */ /*# @@ -289,7 +289,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param namespace String used for selecting external system in which guest user account will be created * @param password String password * @param login String login - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return RichMember sponsored member */ setSponsoredMember { @@ -332,7 +332,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * * If the sponsor is not specified, the current principal becomes the SPONSOR, if he has such privileges. * - * Since there may be error while creating some of the members and we cannot simply rollback the transaction and + * Since there may be error while creating some members, and we cannot simply roll back the transaction and * start over, exceptions during member creation are not thrown and the returned list has this structure: * * [{"name" -> name, "status" -> "OK" or "Error...", "login" -> login, "password" -> password}] @@ -351,11 +351,11 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. - * @param sendActivationLinks (optional) boolean if true link for manual activation of every created sponsored member - * account will be send to the email (can't be used with empty email parameter), default is false + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. + * @param sendActivationLinks boolean (optional) If true link for manual activation of every created sponsored member + * account will be sent to the email (can't be used with empty email parameter), default is false * If set to true, a non-empty namespace has to be provided. - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. * @param groups int[] group ids, to which will be the created users assigned (has to be from the given vo) * @return List> newly created sponsored member, their password and status of creation */ @@ -416,7 +416,7 @@ public List> call(ApiCaller ac, Deserializer params) throws * Can be called either by a user with role SPONSOR, in that case the user becomes the sponsor, * or by a user with role REGISTRAR that must specify the sponsoring user using ID. * - * Since there may be error while creating some of the members and we cannot simply rollback the transaction and start over, + * Since there may be error while creating some members, and we cannot simply roll back the transaction and start over, * exceptions during member creation are not thrown and the returned list has this structure: * * [{"name" -> name, "status" -> "OK" or "Error...", "login" -> login, "password" -> password}] @@ -431,12 +431,12 @@ public List> call(ApiCaller ac, Deserializer params) throws * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param email (optional) preferred email that will be set to the created user. If no email - * is provided, "no-reply@muni.cz" is used. - * @param sendActivationLink (optional) boolean if true link for manual activation of every created sponsored member account will be send + * @param email String (optional) preferred email that will be set to the created user. If no email + * is provided, default from the instance config is used (usually some kind of "no-reply" address). + * @param sendActivationLink boolean (optional) If true link for manual activation of every created sponsored member account will be sent * to the email, be careful when using with empty (no-reply) email, default is false - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return List> newly created sponsored member, their password and status of creation */ createSponsoredMembers { From 65c2a3324abaac3b33d507842541531ac6542cef Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Mon, 23 Jan 2023 13:25:20 +0100 Subject: [PATCH 07/28] ci(githubci): ignore renovate branches by semantic release Renovate bot names its branches with .x at the end, which semantic release detected as release branches. Added renovate branches to the list of ignored branches for semantic release CI worflow. --- .github/workflows/semantic-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml index fee61b1631..c3edb12f21 100644 --- a/.github/workflows/semantic-release.yml +++ b/.github/workflows/semantic-release.yml @@ -3,6 +3,7 @@ on: push: branches-ignore: - master + - renovate/* tags-ignore: - v* jobs: From 4b2d66a0171eb07bab84e7c09c049578588d624f Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Fri, 20 Jan 2023 15:31:36 +0100 Subject: [PATCH 08/28] fix(core): fixed sending emails in batches to MSMTP MSMTP implementation of SMTP daemon cannot receive more than a single message in one session. Sending batches of messages was reimplemented to send each message separately. Many classes were not used or were direct copies of Spring classes, thus they were removed to simplify code. --- .../dao/jdbc/PerunNotifObjectDaoImpl.java | 17 +- .../dao/jdbc/PerunNotifRegexDaoImpl.java | 31 +- .../dao/jdbc/PerunNotifTemplateDaoImpl.java | 14 +- .../perun/notif/mail/Authenticator.java | 23 -- .../perun/notif/mail/EmailMessage.java | 30 -- .../notif/mail/EmailPreparationException.java | 28 -- .../perun/notif/mail/MessagePreparator.java | 325 ------------------ .../notif/mail/MimeMessagePreparator.java | 43 --- .../notif/mail/PerunNotifHTMLMessage.java | 12 - .../notif/mail/PerunNotifPlainMessage.java | 12 - .../EmailAuthenticationException.java | 37 -- .../notif/mail/exception/EmailException.java | 31 -- .../mail/exception/EmailParseException.java | 38 -- .../mail/exception/EmailSendException.java | 183 ---------- .../managers/PerunNotifEmailManagerImpl.java | 317 ++--------------- .../PerunNotifPoolMessageManagerImpl.java | 5 +- .../PerunNotifTemplateManagerImpl.java | 2 - 17 files changed, 56 insertions(+), 1092 deletions(-) delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/Authenticator.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailMessage.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailPreparationException.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MessagePreparator.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MimeMessagePreparator.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifHTMLMessage.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifPlainMessage.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailAuthenticationException.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailException.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailParseException.java delete mode 100644 perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailSendException.java diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifObjectDaoImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifObjectDaoImpl.java index 33d89e703c..c45875c7ad 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifObjectDaoImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifObjectDaoImpl.java @@ -1,6 +1,5 @@ package cz.metacentrum.perun.notif.dao.jdbc; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.impl.Utils; import cz.metacentrum.perun.notif.dao.PerunNotifObjectDao; import cz.metacentrum.perun.notif.entities.PerunNotifObject; @@ -11,12 +10,10 @@ import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Repository; -import java.util.Arrays; import java.util.List; /** * Jdbc implementation of PerunNotifObjectDao - * * User: tomastunkl Date: 01.11.12 Time: 22:57 */ @Repository("perunNotifObjectDao") @@ -63,7 +60,7 @@ public PerunNotifObject getPerunNotifObjectById(int id) { logger.debug("Getting PerunNotifObject from db by id: {}", id); try { - PerunNotifObject object = this.getJdbcTemplate().queryForObject("SELECT * FROM pn_object object where object.id = ?", new Object[]{id}, PerunNotifObject.PERUN_NOTIF_OBJECT); + PerunNotifObject object = this.getJdbcTemplate().queryForObject("SELECT * FROM pn_object object where object.id = ?", PerunNotifObject.PERUN_NOTIF_OBJECT, id); logger.debug("PerunNotifObject retrieved from db: {}", object); return object; } catch (EmptyResultDataAccessException ex) { @@ -75,14 +72,14 @@ public PerunNotifObject getPerunNotifObjectById(int id) { @Override public boolean isObjectRelation(int templateId, Integer objectId) { - logger.debug("IsObjectRelation for templateId: {}, objectId: {}", Arrays.asList(templateId, objectId)); + logger.debug("IsObjectRelation for templateId: {}, objectId: {}", templateId, objectId); try { SqlRowSet rowSet = this.getJdbcTemplate().queryForRowSet("select * from pn_regex_object where regex_id = ? AND object_id = ?", templateId, objectId); - logger.debug("Relation between templateId: {} and objectId: {}, found.", Arrays.asList(templateId, objectId)); + logger.debug("Relation between templateId: {} and objectId: {}, found.", templateId, objectId); return rowSet.next(); } catch (EmptyResultDataAccessException ex) { //This exception signals empty row - logger.debug("Relation between templateId: {}, and objectId: {}, not found", Arrays.asList(templateId, objectId)); + logger.debug("Relation between templateId: {}, and objectId: {}, not found", templateId, objectId); return false; } } @@ -90,17 +87,17 @@ public boolean isObjectRelation(int templateId, Integer objectId) { @Override public void saveObjectRelation(int templateId, Integer objectId) { - logger.debug("Saving relation bewteen templateId: {}, and objectId: {} to db.", Arrays.asList(templateId, objectId)); + logger.debug("Saving relation bewteen templateId: {}, and objectId: {} to db.", templateId, objectId); int newId = Utils.getNewId(this.getJdbcTemplate(), "pn_regex_object_seq"); this.getJdbcTemplate().update("insert into pn_regex_object(id, regex_id, object_id) values(?,?,?)", newId, templateId, objectId); - logger.debug("Relation between templateId: {} and objectId: {} saved to db with id: {}", Arrays.asList(templateId, objectId, newId)); + logger.debug("Relation between templateId: {} and objectId: {} saved to db with id: {}", templateId, objectId, newId); } @Override public void removePerunNotifObjectRegexRelation(int regexId, int objectId) { - logger.debug("Removing relation between object: {} and regex: {} from db.", Arrays.asList(objectId, regexId)); + logger.debug("Removing relation between object: {} and regex: {} from db.", objectId, regexId); this.getJdbcTemplate().update("delete from pn_regex_object where regex_id = ? and object_id = ?", regexId, objectId); } diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifRegexDaoImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifRegexDaoImpl.java index ea6d8b6663..7b833bc72b 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifRegexDaoImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifRegexDaoImpl.java @@ -1,6 +1,5 @@ package cz.metacentrum.perun.notif.dao.jdbc; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.impl.Utils; import cz.metacentrum.perun.notif.dao.PerunNotifRegexDao; import cz.metacentrum.perun.notif.entities.PerunNotifObject; @@ -8,13 +7,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; -import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Repository; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -38,7 +33,7 @@ public List getAll() { logger.debug("Regexes loaded from db: {}, loading objects.", result); for (PerunNotifRegex regex : result) { - List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", new Object[]{regex.getId()}, PerunNotifObject.PERUN_NOTIF_OBJECT); + List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", PerunNotifObject.PERUN_NOTIF_OBJECT, regex.getId()); regex.addObjects(objects); } logger.debug("Objects loaded, resulting regexes: {}", result); @@ -65,14 +60,14 @@ public PerunNotifRegex getPerunNotifRegexById(int id) { logger.debug("Loading regex from db by id: {}", id); PerunNotifRegex regex = null; try { - regex = this.getJdbcTemplate().queryForObject("SELECT * from pn_regex where id = ?", new Object[]{id}, PerunNotifRegex.PERUN_NOTIF_REGEX); + regex = this.getJdbcTemplate().queryForObject("SELECT * from pn_regex where id = ?", PerunNotifRegex.PERUN_NOTIF_REGEX, id); } catch (EmptyResultDataAccessException ex) { logger.debug("Regex with id: {}, not found.", id); return null; } logger.debug("Regex with id: {}, loaded from db. Loading objects.", id); - List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", new Object[]{regex.getId()}, PerunNotifObject.PERUN_NOTIF_OBJECT); + List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", PerunNotifObject.PERUN_NOTIF_OBJECT, regex.getId()); regex.addObjects(objects); logger.debug("Objects loaded result: {}", regex); @@ -83,15 +78,15 @@ public PerunNotifRegex getPerunNotifRegexById(int id) { public Set getPerunNotifRegexForTemplateId(int id) { logger.debug("Loading regexes for template with id: {}", id); - List regexes = this.getJdbcTemplate().query("SELECT * from pn_regex regex JOIN pn_template_regex bind ON regex.id=bind.regex_id WHERE bind.template_id = ?", new Object[]{id}, PerunNotifRegex.PERUN_NOTIF_REGEX); + List regexes = this.getJdbcTemplate().query("SELECT * from pn_regex regex JOIN pn_template_regex bind ON regex.id=bind.regex_id WHERE bind.template_id = ?", PerunNotifRegex.PERUN_NOTIF_REGEX, id); if (regexes != null) { for (PerunNotifRegex regex : regexes) { - List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", new Object[]{regex.getId()}, PerunNotifObject.PERUN_NOTIF_OBJECT); + List objects = this.getJdbcTemplate().query("SELECT * from pn_object object JOIN pn_regex_object bind ON object.id = bind.object_id WHERE bind.regex_id = ?", PerunNotifObject.PERUN_NOTIF_OBJECT, regex.getId()); regex.addObjects(objects); } } - logger.debug("Regexes loaded with object for template id: {}, result: {}", Arrays.asList(id, regexes)); + logger.debug("Regexes loaded with object for template id: {}, result: {}", id, regexes); return new HashSet(regexes); } @@ -119,19 +114,13 @@ public void removePerunNotifRegexById(int id) { public List getTemplateIdsForRegexId(int id) { logger.debug("Loading templateIds for regexId: {}", id); - return this.getJdbcTemplate().query("select template_id from pn_template_regex where regex_id = ?", new Object[]{id}, new RowMapper() { - @Override - public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { - - return rs.getInt("template_id"); - } - }); + return this.getJdbcTemplate().query("select template_id from pn_template_regex where regex_id = ?", (rs, rowNum) -> rs.getInt("template_id"), id); } @Override public boolean isRegexRelation(int templateId, Integer regexId) { - logger.debug("Trying to load relation between template: {}, and regex: {}", Arrays.asList(templateId, regexId)); + logger.debug("Trying to load relation between template: {}, and regex: {}", templateId, regexId); int countOfRelations = this.getJdbcTemplate().queryForObject( "select count (*) from pn_template_regex where template_id = ? AND regex_id = ?", Integer.class, templateId, regexId); return countOfRelations > 0; @@ -140,7 +129,7 @@ public boolean isRegexRelation(int templateId, Integer regexId) { @Override public void saveTemplateRegexRelation(int templateId, Integer regexId) { - logger.debug("Saving relation between template: {} and regex: {}", Arrays.asList(templateId, regexId)); + logger.debug("Saving relation between template: {} and regex: {}", templateId, regexId); int newId = Utils.getNewId(this.getJdbcTemplate(), "pn_template_regex_seq"); this.getJdbcTemplate().update("insert into pn_template_regex(id, template_id, regex_id) values(?,?,?)", newId, templateId, regexId); @@ -150,7 +139,7 @@ public void saveTemplateRegexRelation(int templateId, Integer regexId) { @Override public void removePerunNotifTemplateRegexRelation(int templateId, int regexId) { - logger.debug("Removing relation between template: {} and regex: {}", Arrays.asList(templateId, regexId)); + logger.debug("Removing relation between template: {} and regex: {}", templateId, regexId); this.getJdbcTemplate().update("delete from pn_template_regex where template_id = ? and regex_id = ? ", templateId, regexId); } } diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifTemplateDaoImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifTemplateDaoImpl.java index 6d29c2b12d..dc77f8b654 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifTemplateDaoImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/dao/jdbc/PerunNotifTemplateDaoImpl.java @@ -34,10 +34,10 @@ public List getAllPerunNotifTemplates() { Set perunNotifRegexs = perunNotifRegexDao.getPerunNotifRegexForTemplateId(template.getId()); template.setMatchingRegexs(perunNotifRegexs); - List perunNotifReceiver = this.getJdbcTemplate().query("SELECT * from pn_receiver where template_id = ?", new Object[]{template.getId()}, PerunNotifReceiver.PERUN_NOTIF_RECEIVER); + List perunNotifReceiver = this.getJdbcTemplate().query("SELECT * from pn_receiver where template_id = ?", PerunNotifReceiver.PERUN_NOTIF_RECEIVER, template.getId()); template.setReceivers(perunNotifReceiver); - List perunNotifTemplateMessages = this.getJdbcTemplate().query("SELECT * from pn_template_message where template_id = ?", new Object[]{template.getId()}, PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER); + List perunNotifTemplateMessages = this.getJdbcTemplate().query("SELECT * from pn_template_message where template_id = ?", PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER, template.getId()); template.setPerunNotifTemplateMessages(perunNotifTemplateMessages); } @@ -74,7 +74,7 @@ public PerunNotifTemplate updatePerunNotifTemplateData(PerunNotifTemplate templa public PerunNotifReceiver getPerunNotifReceiverById(int id) { try { - PerunNotifReceiver object = this.getJdbcTemplate().queryForObject("select * from pn_receiver where id = ?", new Object[]{id}, PerunNotifReceiver.PERUN_NOTIF_RECEIVER); + PerunNotifReceiver object = this.getJdbcTemplate().queryForObject("select * from pn_receiver where id = ?", PerunNotifReceiver.PERUN_NOTIF_RECEIVER, id); return object; } catch (EmptyResultDataAccessException ex) { return null; @@ -112,7 +112,7 @@ public PerunNotifTemplate getPerunNotifTemplateById(int id) { PerunNotifTemplate template = null; try { - template = this.getJdbcTemplate().queryForObject("SELECT * from pn_template where id = ?", new Object[]{id}, PerunNotifTemplate.PERUN_NOTIF_TEMPLATE); + template = this.getJdbcTemplate().queryForObject("SELECT * from pn_template where id = ?", PerunNotifTemplate.PERUN_NOTIF_TEMPLATE, id); } catch (EmptyResultDataAccessException ex) { //This exception is thrown when object is not found return null; @@ -121,10 +121,10 @@ public PerunNotifTemplate getPerunNotifTemplateById(int id) { Set regexes = perunNotifRegexDao.getPerunNotifRegexForTemplateId(template.getId()); template.setMatchingRegexs(regexes); - List perunNotifReceiver = this.getJdbcTemplate().query("SELECT * from pn_receiver where template_id = ?", new Object[]{template.getId()}, PerunNotifReceiver.PERUN_NOTIF_RECEIVER); + List perunNotifReceiver = this.getJdbcTemplate().query("SELECT * from pn_receiver where template_id = ?", PerunNotifReceiver.PERUN_NOTIF_RECEIVER, template.getId()); template.setReceivers(perunNotifReceiver); - List perunNotifTemplateMessages = this.getJdbcTemplate().query("SELECT * from pn_template_message where template_id = ?", new Object[]{template.getId()}, PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER); + List perunNotifTemplateMessages = this.getJdbcTemplate().query("SELECT * from pn_template_message where template_id = ?", PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER, template.getId()); template.setPerunNotifTemplateMessages(perunNotifTemplateMessages); return template; @@ -151,7 +151,7 @@ public void removePerunNotifReceiverById(int id) { public PerunNotifTemplateMessage getPerunNotifTemplateMessageById(int id) { try { - return this.getJdbcTemplate().queryForObject("select * from pn_template_message where id = ?", new Object[]{id}, PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER); + return this.getJdbcTemplate().queryForObject("select * from pn_template_message where id = ?", PerunNotifTemplateMessage.PERUN_NOTIF_TEMPLATE_MESSAGE_ROW_MAPPER, id); } catch (EmptyResultDataAccessException ex) { return null; } diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/Authenticator.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/Authenticator.java deleted file mode 100644 index dc42b184cd..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/Authenticator.java +++ /dev/null @@ -1,23 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -import javax.mail.PasswordAuthentication; - -/** - * Implementation of javax.mail.Authenticator, hodls holder of - * passwordAuthentication - * - * @author tomas.tunkl - * - */ -public class Authenticator extends javax.mail.Authenticator { - - private PasswordAuthentication passwordAuthentication; - - public Authenticator(String username, String password) { - passwordAuthentication = new PasswordAuthentication(username, password); - } - - protected PasswordAuthentication getPasswordAuthentication() { - return passwordAuthentication; - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailMessage.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailMessage.java deleted file mode 100644 index a3c742ea39..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailMessage.java +++ /dev/null @@ -1,30 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -import java.util.List; - -/** - * Interface for email message. Contains TO, CC, BCC - */ -public interface EmailMessage extends MimeMessagePreparator { - - /** - * Who we send email TO - * - * @return - */ - List getTo(); - - /** - * Who we send email in CC - * - * @return - */ - List getCc(); - - /** - * Who we send email in BCC - * - * @return - */ - List getBcc(); -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailPreparationException.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailPreparationException.java deleted file mode 100644 index 56a54f66ef..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/EmailPreparationException.java +++ /dev/null @@ -1,28 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -/** - * Exception is thrown when error occurs during preparing of mail from template - * - * @author tomas.tunkl - * - */ -public class EmailPreparationException extends RuntimeException { - - private static final long serialVersionUID = 195888069765206588L; - - public EmailPreparationException() { - super(); - } - - public EmailPreparationException(String msg) { - super(msg); - } - - public EmailPreparationException(Throwable cause) { - super(cause); - } - - public EmailPreparationException(String msg, Throwable cause) { - super(msg, cause); - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MessagePreparator.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MessagePreparator.java deleted file mode 100644 index fe79dcf845..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MessagePreparator.java +++ /dev/null @@ -1,325 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -import freemarker.template.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.activation.DataHandler; -import javax.activation.DataSource; -import javax.activation.FileDataSource; -import javax.mail.BodyPart; -import javax.mail.Message; -import javax.mail.Multipart; -import javax.mail.Part; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -/** - * Message preparator for preparing messages in utf-8 - * - * inspired from - * http://opensource.atlassian.com/confluence/spring/display/DISC/Sending+FreeMarker-based+multipart+email+with+Spring - */ -public class MessagePreparator implements MimeMessagePreparator { - - private static final Logger logger = LoggerFactory.getLogger(MessagePreparator.class); - - /** - * Email type - */ - public static enum EmailType { - - PLAIN, HTML; - } - - private List to = new ArrayList(); - private List cc = new ArrayList(); - private List bcc = new ArrayList(); - private String content = null; - private Configuration configuration = null; - private String from = ""; - private String fromText = ""; - private String subject = ""; - private List fileNames = new ArrayList(); - private EmailType mailType = null; - - /** - * Constructor - * - * @param from - * @param subject - * @param content - * @param mailType - */ - public MessagePreparator(String from, String fromText, String subject, String content, EmailType mailType) { - this.content = content; - this.from = from; - this.fromText = fromText; - this.subject = subject; - this.mailType = mailType; - } - - - /* - * (non-Javadoc) - * - * @see org.springframework.mail.javamail.MimeMessagePreparator#prepare(javax.mail.internet.MimeMessage) - */ - public void prepare(MimeMessage mimeMessage) throws Exception { - MimeMultipart mpRoot = new MimeMultipart("mixed"); - Multipart mp = new MimeMultipart("alternative"); - - // Create a body part to house the multipart/alternative Part - MimeBodyPart contentPartRoot = new MimeBodyPart(); - contentPartRoot.setContent(mp); - - // Add the root body part to the root multipart - mpRoot.addBodyPart(contentPartRoot); - - // adding recipients, cc and bcc - if (getTo() != null) { - for (String to : getTo()) { - mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); - } - } - - if (getCc() != null) { - for (String cc : getCc()) { - mimeMessage.addRecipient(Message.RecipientType.CC, new InternetAddress(cc)); - } - } - - if (getBcc() != null) { - for (String bcc : getBcc()) { - mimeMessage.addRecipient(Message.RecipientType.BCC, new InternetAddress(bcc)); - } - } - - // adding from and subject - mimeMessage.setFrom(new InternetAddress(getFrom())); - mimeMessage.setSubject(getSubject()); - - if (getEmailType().equals(EmailType.HTML)) { - mp.addBodyPart(createHtmlMessage()); - } else if (getEmailType().equals(EmailType.PLAIN)) { - mp.addBodyPart(createTextMessage()); - } - - // Create an "ATTACHMENT" - we must put it to mpRoot(mixed content) - if (getFileNames() != null) { - for (String filename : getFileNames()) { - mpRoot.addBodyPart(createAttachment(filename)); - } - } - - mimeMessage.setContent(mpRoot); - - logger.debug("Message is prepared to send"); - } - - /** - * Creates plain text message from freemarker template - * - * @return - * @throws Exception - */ - private BodyPart createTextMessage() throws Exception { - BodyPart textPart = new MimeBodyPart(); - - textPart.setDataHandler(createDataHandler(content.getBytes(StandardCharsets.UTF_8), "text/plain;charset=utf-8")); - - logger.debug("TEXT MESSAGE CREATED ; content: " + content); - - return textPart; - } - - /** - * Creates HTML message from freemarker template - * - * @return - * @throws Exception - */ - private BodyPart createHtmlMessage() throws Exception { - Multipart htmlContent = new MimeMultipart("related"); - BodyPart htmlPage = new MimeBodyPart(); - - htmlPage.setDataHandler(createDataHandler(content.getBytes(StandardCharsets.UTF_8), "text/html;charset=utf-8")); - - htmlContent.addBodyPart(htmlPage); - BodyPart htmlPart = new MimeBodyPart(); - htmlPart.setContent(htmlContent); - - logger.debug("HTML MESSAGE CREATED ; content: " + content); - - return htmlPart; - } - - /** - * Creates attachment from filename - * - * @param filename - * @return - * @throws Exception - */ - private BodyPart createAttachment(String filename) throws Exception { - BodyPart attachBodypart = new MimeBodyPart(); - File file = new File(filename); - FileDataSource fds = new FileDataSource(file); - DataHandler dh = new DataHandler(fds); - - attachBodypart.setFileName(file.getName()); - attachBodypart.setDisposition(Part.ATTACHMENT); - attachBodypart.setDescription("Attached file: " + file.getName()); - attachBodypart.setDataHandler(dh); - - logger.debug("ATTACHMENT ADDED ; filename: " + filename); - - return attachBodypart; - } - - /** - * Creates datahandler for both plain text and HTML messages - * - * @param stringBytes - * @param contentType - * @return - */ - private DataHandler createDataHandler(final byte[] stringBytes, final String contentType) { - return new DataHandler(new DataSource() { - - public InputStream getInputStream() throws IOException { - return new BufferedInputStream(new ByteArrayInputStream(stringBytes)); - } - - public OutputStream getOutputStream() throws IOException { - throw new IOException("Read-only data"); - } - - public String getContentType() { - return contentType; - } - - public String getName() { - return "main"; - } - }); - } - - /** - * Add atachment filename - * - * @param filename - */ - public void addFileName(String filename) { - this.fileNames.add(filename); - } - - /** - * Add recipient bcc - * - * @param recipientBcc - */ - public void addRecipientBcc(String recipientBcc) { - this.bcc.add(recipientBcc); - } - - /** - * Add recipient cc - * - * @param recipientCc - */ - public void addRecipientCc(String recipientCc) { - this.cc.add(recipientCc); - } - - /** - * Add recipient to - * - * @param recipientTo - */ - public void addRecipientTo(String recipientTo) { - this.to.add(recipientTo); - } - - /** - * Clear recipient bcc - */ - public void clearRecipientBcc() { - this.bcc.clear(); - } - - /** - * Clear recipient cc - */ - public void clearRecipientCc() { - this.cc.clear(); - } - - /** - * Clear filename - */ - public void clearRecipientFileName() { - this.fileNames.clear(); - } - - /** - * Clear recipient to - */ - public void clearRecipientTo() { - this.to.clear(); - } - - public List getTo() { - return to; - } - - public List getCc() { - return cc; - } - - public List getBcc() { - return bcc; - } - - public Configuration getConfiguration() { - return configuration; - } - - public void setConfiguration(Configuration configuration) { - this.configuration = configuration; - } - - public String getFrom() { - return from; - } - - public String getSubject() { - return subject; - } - - public String getContent() { - return content; - } - - public List getFileNames() { - return fileNames; - } - - public EmailType getEmailType() { - return mailType; - } - - public String getFromText() { - return fromText; - } - - public void setFromText(String fromText) { - this.fromText = fromText; - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MimeMessagePreparator.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MimeMessagePreparator.java deleted file mode 100644 index a62da0017c..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/MimeMessagePreparator.java +++ /dev/null @@ -1,43 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -import javax.mail.internet.MimeMessage; - -/** - * Callback interface for the preparation of JavaMail MIME messages. - * - *

- * The corresponding send methods of - * {@link org.springframework.mail.javamail.JavaMailSender} will take care of - * the actual creation of a {@link MimeMessage} instance, and of proper - * exception conversion. - * - *

- * It is often convenient to use a - * {@link org.springframework.mail.javamail.MimeMessageHelper} for populating - * the passed-in MimeMessage, in particular when working with attachments or - * special character encodings. See - * {@link org.springframework.mail.javamail.MimeMessageHelper MimeMessageHelper's javadoc} - * for an example. - * - * @author Juergen Hoeller - * @since 07.10.2003 - * @see org.springframework.mail.javamail.MimeMessageHelper - */ -public interface MimeMessagePreparator { - - /** - * Prepare the given new MimeMessage instance. - * - * @param mimeMessage the message to prepare - * @throws javax.mail.MessagingException passing any exceptions thrown - * by MimeMessage methods through for automatic conversion to the - * EmailException hierarchy - * @throws java.io.IOException passing any exceptions thrown by - * MimeMessage methods through for automatic conversion to the - * EmailException hierarchy - * @throws Exception if mail preparation failed, for example when a - * Velocity template cannot be rendered for the mail text - */ - void prepare(MimeMessage mimeMessage) throws Exception; - -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifHTMLMessage.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifHTMLMessage.java deleted file mode 100644 index 775694a762..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifHTMLMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -/** - * Implementation of html email message - */ -public class PerunNotifHTMLMessage extends MessagePreparator implements EmailMessage { - - public PerunNotifHTMLMessage(String from, String fromText, String subject, String messageContent) { - - super(from, fromText, subject, messageContent, EmailType.HTML); - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifPlainMessage.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifPlainMessage.java deleted file mode 100644 index 45e792ad76..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/PerunNotifPlainMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -package cz.metacentrum.perun.notif.mail; - -/** - * User: tomastunkl Date: 14.10.12 Time: 0:18 - */ -public class PerunNotifPlainMessage extends MessagePreparator implements EmailMessage { - - public PerunNotifPlainMessage(String from, String fromText, String subject, String messageContent) { - - super(from, fromText, subject, messageContent, EmailType.PLAIN); - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailAuthenticationException.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailAuthenticationException.java deleted file mode 100644 index 492277c527..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailAuthenticationException.java +++ /dev/null @@ -1,37 +0,0 @@ -package cz.metacentrum.perun.notif.mail.exception; - -/** - * Exception thrown on failed authentication. - */ -public class EmailAuthenticationException extends EmailException { - - private static final long serialVersionUID = -3518304048940117166L; - - /** - * Constructor for EmailAuthenticationException. - * - * @param msg message - */ - public EmailAuthenticationException(String msg) { - super(msg); - } - - /** - * Constructor for EmailAuthenticationException. - * - * @param msg the detail message - * @param cause the root cause from the mail API in use - */ - public EmailAuthenticationException(String msg, Throwable cause) { - super(msg, cause); - } - - /** - * Constructor for EmailAuthenticationException. - * - * @param cause the root cause from the mail API in use - */ - public EmailAuthenticationException(Throwable cause) { - super("Authentication failed", cause); - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailException.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailException.java deleted file mode 100644 index 19d0adc1b1..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailException.java +++ /dev/null @@ -1,31 +0,0 @@ -package cz.metacentrum.perun.notif.mail.exception; - -/** - * Abstract class for Mail exception. - * - * @author tomas.tunkl - * - */ -public abstract class EmailException extends RuntimeException { - - private static final long serialVersionUID = -1815506118987744701L; - - /** - * Constructor for EmailException. - * - * @param msg the detail message - */ - public EmailException(String msg) { - super(msg); - } - - /** - * Constructor for EmailException. - * - * @param msg the detail message - * @param cause the root cause from the mail API in use - */ - public EmailException(String msg, Throwable cause) { - super(msg, cause); - } -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailParseException.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailParseException.java deleted file mode 100644 index 2284e22e31..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailParseException.java +++ /dev/null @@ -1,38 +0,0 @@ -package cz.metacentrum.perun.notif.mail.exception; - -/** - * Exception thrown if illegal message properties are encountered. - */ -public class EmailParseException extends EmailException { - - private static final long serialVersionUID = 5097798456468717059L; - - /** - * Constructor for EmailParseException. - * - * @param msg the detail message - */ - public EmailParseException(String msg) { - super(msg); - } - - /** - * Constructor for EmailParseException. - * - * @param msg the detail message - * @param cause the root cause from the mail API in use - */ - public EmailParseException(String msg, Throwable cause) { - super(msg, cause); - } - - /** - * Constructor for EmailParseException. - * - * @param cause the root cause from the mail API in use - */ - public EmailParseException(Throwable cause) { - super("Could not parse mail", cause); - } - -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailSendException.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailSendException.java deleted file mode 100644 index 9beda9f89f..0000000000 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/mail/exception/EmailSendException.java +++ /dev/null @@ -1,183 +0,0 @@ -package cz.metacentrum.perun.notif.mail.exception; - -import java.io.PrintStream; -import java.io.PrintWriter; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Exception thrown when a mail sending error is encountered. Can register - * failed messages with their exceptions. - * - * @author Dmitriy Kopylenko - * @author Juergen Hoeller - */ -public class EmailSendException extends EmailException { - - private static final long serialVersionUID = 3143595485706769459L; - - private transient final Map failedMessages; - - private Exception[] messageExceptions; - - /** - * Constructor for EmailSendException. - * - * @param msg the detail message - */ - public EmailSendException(String msg) { - this(msg, null); - } - - /** - * Constructor for EmailSendException. - * - * @param msg the detail message - * @param cause the root cause from the mail API in use - */ - public EmailSendException(String msg, Throwable cause) { - super(msg, cause); - this.failedMessages = new LinkedHashMap(); - } - - /** - * Constructor for registration of failed messages, with the messages - * that failed as keys, and the thrown exceptions as values. - *

- * The messages should be the same that were originally passed to the - * invoked send method. - * - * @param msg the detail message - * @param cause the root cause from the mail API in use - * @param failedMessages Map of failed messages as keys and thrown - * exceptions as values - */ - public EmailSendException(String msg, Throwable cause, Map failedMessages) { - super(msg, cause); - this.failedMessages = new LinkedHashMap(failedMessages); - this.messageExceptions = failedMessages.values().toArray(new Exception[failedMessages.size()]); - } - - /** - * Constructor for registration of failed messages, with the messages - * that failed as keys, and the thrown exceptions as values. - *

- * The messages should be the same that were originally passed to the - * invoked send method. - * - * @param failedMessages Map of failed messages as keys and thrown - * exceptions as values - */ - public EmailSendException(Map failedMessages) { - this(null, null, failedMessages); - } - - /** - * Return a Map with the failed messages as keys, and the thrown - * exceptions as values. - *

- * Note that a general mail server connection failure will not result in - * failed messages being returned here: A message will only be contained - * here if actually sending it was attempted but failed. - *

- * The messages will be the same that were originally passed to the - * invoked send method, that is, SimpleMailMessages in case of using the - * generic MailSender interface. - *

- * In case of sending MimeMessage instances via JavaMailSender, the - * messages will be of type MimeMessage. - *

- * NOTE: This Map will not be available after serialization. Use - * {@link #getMessageExceptions()} in such a scenario, which will be - * available after serialization as well. - * - * @return the Map of failed messages as keys and thrown exceptions as - * values - * @see org.springframework.mail.SimpleMailMessage - * @see javax.mail.internet.MimeMessage - */ - public final Map getFailedMessages() { - return this.failedMessages; - } - - /** - * Return an array with thrown message exceptions. - *

- * Note that a general mail server connection failure will not result in - * failed messages being returned here: A message will only be contained - * here if actually sending it was attempted but failed. - * - * @return the array of thrown message exceptions, or an empty array if - * no failed messages - */ - public final Exception[] getMessageExceptions() { - return (this.messageExceptions != null ? this.messageExceptions : new Exception[0]); - } - - @Override - public String getMessage() { - if (this.messageExceptions == null || this.messageExceptions.length < 1) { - return super.getMessage(); - } else { - StringBuilder sb = new StringBuilder(); - String baseMessage = super.getMessage(); - if (baseMessage != null) { - sb.append(baseMessage).append(". "); - } - sb.append("Failed messages: "); - for (int i = 0; i < this.messageExceptions.length; i++) { - Exception subEx = this.messageExceptions[i]; - sb.append(subEx.toString()); - if (i < this.messageExceptions.length - 1) { - sb.append("; "); - } - } - return sb.toString(); - } - } - - @Override - public String toString() { - if (this.messageExceptions == null || this.messageExceptions.length < 1) { - return super.toString(); - } else { - StringBuilder sb = new StringBuilder(super.toString()); - sb.append("; message exceptions (").append(this.messageExceptions.length).append(") are:"); - for (int i = 0; i < this.messageExceptions.length; i++) { - Exception subEx = this.messageExceptions[i]; - sb.append('\n').append("Failed message ").append(i + 1).append(": "); - sb.append(subEx); - } - return sb.toString(); - } - } - - @Override - public void printStackTrace(PrintStream ps) { - if (this.messageExceptions == null || this.messageExceptions.length < 1) { - super.printStackTrace(ps); - } else { - ps.println(super.toString() + "; message exception details (" + this.messageExceptions.length + ") are:"); - for (int i = 0; i < this.messageExceptions.length; i++) { - Exception subEx = this.messageExceptions[i]; - ps.println("Failed message " + (i + 1) + ":"); - subEx.printStackTrace(ps); - } - } - } - - @Override - public void printStackTrace(PrintWriter pw) { - if (this.messageExceptions == null || this.messageExceptions.length < 1) { - super.printStackTrace(pw); - } else { - pw.println(super.toString() + "; message exception details (" + this.messageExceptions.length + ") are:"); - for (int i = 0; i < this.messageExceptions.length; i++) { - Exception subEx = this.messageExceptions[i]; - pw.println("Failed message " + (i + 1) + ":"); - subEx.printStackTrace(pw); - } - } - } - -} diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java index d052aa17eb..6dd5644dd3 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java @@ -2,46 +2,25 @@ import cz.metacentrum.perun.core.api.BeansUtils; import cz.metacentrum.perun.notif.dto.PerunNotifEmailMessageToSendDto; -import cz.metacentrum.perun.notif.mail.Authenticator; -import cz.metacentrum.perun.notif.mail.EmailPreparationException; -import cz.metacentrum.perun.notif.mail.PerunNotifPlainMessage; -import cz.metacentrum.perun.notif.mail.exception.EmailAuthenticationException; -import cz.metacentrum.perun.notif.mail.exception.EmailException; -import cz.metacentrum.perun.notif.mail.exception.EmailSendException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.MailException; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import javax.mail.*; -import javax.mail.internet.MimeMessage; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.List; +import java.util.Properties; -@org.springframework.stereotype.Service("perunNotifEmailManager") +@Service("perunNotifEmailManager") public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { @Autowired private Properties propertiesBean; - /** - * The default protocol: 'smtp' - */ - public static final String DEFAULT_PROTOCOL = "smtp"; - private String protocol = DEFAULT_PROTOCOL; - - private Session session; - private boolean mailSmtpAuth; - private String username; - private String password; - private String smtpHost; - private int port; - private String emailFrom; - private String fromText; private boolean sendMessages; - private boolean startTls; - private boolean mailDebug; private static final Logger logger = LoggerFactory.getLogger(PerunNotifEmailManager.class); @@ -50,278 +29,42 @@ public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { @PostConstruct public void init() throws Exception { - - System.setProperty("file.encoding", "utf-8"); - System.setProperty("mail.mime.charset", "utf-8"); - System.setProperty("client.encoding", "utf-8"); - - // Load properties file notif.properties - this.mailSmtpAuth = BeansUtils.getCoreConfig().isSmtpAuth(); - this.username = BeansUtils.getCoreConfig().getSmtpUser(); - this.password = BeansUtils.getCoreConfig().getSmtpPass(); - this.smtpHost = BeansUtils.getCoreConfig().getSmtpHost(); - this.port = BeansUtils.getCoreConfig().getSmtpPort(); - if (this.port <= 0) { - // sane default - this.port = 25; - } - this.mailDebug = BeansUtils.getCoreConfig().isMailDebug(); - this.startTls = BeansUtils.getCoreConfig().isSmtpStartTls(); - this.emailFrom = (String) propertiesBean.get("notif.emailFrom"); - this.fromText = (String) propertiesBean.get("notif.fromText"); String sendMessages_s = (String) propertiesBean.get("notif.sendMessages"); - this.sendMessages = sendMessages_s == null ? false : (sendMessages_s.equals("true") ? true : false); - - createSession(); - } - - private void createSession() { - Authenticator authenticator = null; - if (mailSmtpAuth) { - authenticator = new Authenticator(username, password); - } else { - username = null; - password = null; - } - - Properties properties = new Properties(); - - properties.setProperty("mail.smtp.submitter", ""); - properties.setProperty("mail.smtp.host", smtpHost); - properties.setProperty("mail.smtp.port", String.valueOf(port)); - properties.setProperty("mail.smtp.auth", String.valueOf(mailSmtpAuth)); - properties.setProperty("mail.smtp.starttls.enable", String.valueOf(startTls)); - properties.setProperty("mail.debug", String.valueOf(mailDebug)); - - session = Session.getInstance(properties, authenticator); + this.sendMessages = sendMessages_s != null && sendMessages_s.equals("true"); } @Override public void sendMessages(List list) { - - List emailList = new ArrayList(); - for (PerunNotifEmailMessageToSendDto emailMessage : list) { - PerunNotifPlainMessage message = new PerunNotifPlainMessage(emailMessage.getSender(), null, emailMessage.getSubject(), emailMessage.getMessage()); - message.addRecipientTo(emailMessage.getReceiver()); - - emailList.add(message); - } - - sendEmailsInBatch(emailList); - logger.info("Messages successfully sent."); - } - - private void sendEmailsInBatch(List messageList) { - if (!sendMessages) { return; } - - List mimeMessages = new ArrayList(); - // for logging purposes - List messagesContents = new ArrayList(); - - for (PerunNotifPlainMessage emailMessage : messageList) { - logger.debug("SENDING PLAIN MESSAGE ; to: " + emailMessage.getTo() + " ; cc: " + emailMessage.getCc() + " ; bcc: " - + emailMessage.getBcc()); - - MimeMessage mimeMessage = createMimeMessage(); - try { - emailMessage.prepare(mimeMessage); - mimeMessages.add(mimeMessage); - - messagesContents.add(emailMessage.getContent()); - } catch (Exception ex) { - failedEmailLogger.error(emailMessage.toString()); - logger.error("Preparing message to send failed.", ex); - } - } - - try { - doSend(mimeMessages.toArray(new MimeMessage[mimeMessages.size()]), messagesContents); - } catch (EmailSendException ex) { - Map failedMessages = ex.getFailedMessages(); - if (failedMessages != null && !failedMessages.isEmpty()) { - for (Object key : failedMessages.keySet()) { - try { - MimeMessage message = (MimeMessage) key; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - message.writeTo(out); - String str = out.toString(StandardCharsets.UTF_8); - failedEmailLogger.error(str); - } catch (Exception e) { - logger.error("Failed to write log about not sent email.", ex); - } - } - } - logger.error("Sending of the email failed.", ex); - } catch (Exception ex) { - throw new EmailPreparationException(ex); - } - } - - /** - * Actually send the given array of MimeMessages via JavaMail. - * - * @param mimeMessages MimeMessage objects to send - * @throws EmailAuthenticationException in case of authentication - * failure - * @throws EmailSendException in case of failure when sending a message - */ - protected void doSend(MimeMessage[] mimeMessages, List contents) { - Map failedMessages = new LinkedHashMap(); - - Transport transport; try { - transport = getTransport(getSession()); - transport.connect(smtpHost, port, username, password); - } catch (AuthenticationFailedException ex) { - throw new EmailAuthenticationException(ex); - } catch (MessagingException ex) { - // Effectively, all messages failed... - for (int i = 0; i < mimeMessages.length; i++) { - failedMessages.put(mimeMessages[i], ex); - } - throw new EmailSendException("Mail server connection failed", ex, failedMessages); - } - - try { - for (int i = 0; i < mimeMessages.length; i++) { - MimeMessage mimeMessage = mimeMessages[i]; - String content = contents.get(i); + JavaMailSender mailSender = BeansUtils.getDefaultMailSender(); + for (PerunNotifEmailMessageToSendDto dto : list) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(dto.getSender()); + message.setTo(dto.getReceiver()); + message.setSubject(dto.getSubject()); + message.setText(dto.getMessage()); try { - if (mimeMessage.getSentDate() == null) { - mimeMessage.setSentDate(new Date()); - } - mimeMessage.saveChanges(); - transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); - - - sendMessagesLogger.info("Email sent in {} with receivers: " - + Arrays.toString(mimeMessage.getRecipients(Message.RecipientType.TO)) - + " senders: " + Arrays.toString(mimeMessage.getFrom()) - + " subject: " + mimeMessage.getSubject() - + " content: " - + content, - new Date()); - } catch (MessagingException ex) { - try { - logger.error("Error during send of email for: {}", mimeMessage.getRecipients(Message.RecipientType.TO), ex); - } catch (Exception ex2) { - logger.error("Cannot send email.", ex); - } - failedMessages.put(mimeMessage, ex); - } - } - } finally { - try { - transport.close(); - } catch (MessagingException ex) { - if (!failedMessages.isEmpty()) { - throw new EmailSendException("Failed to close server connection after message failures", ex, failedMessages); - } else { - throw new EmailSendException("Failed to close server connection after message sending", ex); + // log to normal logger + logger.info("Sending email to: {}, from: {}, subject: {}", + message.getTo(), message.getFrom(), message.getSubject() + ); + //send message over SMTP + mailSender.send(message); + // log successful sending to sendMessages logger + sendMessagesLogger.info("Email sent to: {}, from: {}, subject: {}, text: {}", + message.getTo(), message.getFrom(), message.getSubject(), dto.getMessage() + ); + } catch (MailException ex) { + failedEmailLogger.error("cannot send email", ex); + failedEmailLogger.error("{}", message); } } + } catch (Exception ex) { + failedEmailLogger.error("sending messages failed", ex); } - - if (!failedMessages.isEmpty()) { - throw new EmailSendException(failedMessages); - } - } - - /** - * Obtain a Transport object from the given JavaMail Session, using the - * configured protocol. - *

- * Can be overridden in subclasses, e.g. to return a mock Transport - * object. - * - * @see javax.mail.Session#getTransport(String) - * @see #getProtocol() - */ - protected Transport getTransport(Session session) throws NoSuchProviderException { - return session.getTransport(protocol); - } - - public MimeMessage createMimeMessage() { - return new MimeMessage(getSession()); - } - - private Session getSession() { - - return session; - } - - public boolean getMailSmtpAuth() { - return mailSmtpAuth; - } - - public void setMailSmtpAuth(boolean mailSmtpAuth) { - this.mailSmtpAuth = mailSmtpAuth; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getSmtpHost() { - return smtpHost; } - public void setSmtpHost(String smtpHost) { - this.smtpHost = smtpHost; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getEmailFrom() { - return emailFrom; - } - - public void setEmailFrom(String emailFrom) { - this.emailFrom = emailFrom; - } - - public String getFromText() { - return fromText; - } - - public void setFromText(String fromText) { - this.fromText = fromText; - } - - public boolean isSendMessage() { - return sendMessages; - } - - public void setSendMessage(boolean sendMessage) { - this.sendMessages = sendMessage; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } } diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifPoolMessageManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifPoolMessageManagerImpl.java index d3d1ea0b3f..1c48127e70 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifPoolMessageManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifPoolMessageManagerImpl.java @@ -3,7 +3,6 @@ import cz.metacentrum.perun.auditparser.AuditParser; import cz.metacentrum.perun.core.api.PerunBean; import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.bl.PerunBl; import cz.metacentrum.perun.notif.utils.NotifUtils; import cz.metacentrum.perun.notif.dao.PerunNotifPoolMessageDao; @@ -295,7 +294,7 @@ private ParsedMethod parseMethod(String className, Integer startPosition) { result.setMethodName(methodName); result.setMethodType(ParsedMethod.MethodType.METHOD); - Integer position = new Integer(i + 1); + int position = i + 1; while (className.length() >= position && className.charAt(position) != ')') { ParsedMethod param = parseMethod(className, position); result.addParam(param); @@ -310,7 +309,7 @@ private ParsedMethod parseMethod(String className, Integer startPosition) { result.setLastPosition(i); return result; } else if (character == '.') { - Integer position = new Integer(i + 1); + Integer position = i + 1; ParsedMethod nextMethod = parseMethod(className, position); result.setNextMethod(nextMethod); result.setMethodName(methodName); diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifTemplateManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifTemplateManagerImpl.java index 317c379c05..3a890a7c84 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifTemplateManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifTemplateManagerImpl.java @@ -297,9 +297,7 @@ public Set processPoolMessages(Integer templateId, List no } if (handlingSender != null) { - logger.debug("Found handling sender: {}", handlingSender.toString()); processedIds.addAll(handlingSender.send(messagesToSend.get(typeOfReceiver))); - logger.debug("Messages send by sender: {}", handlingSender.toString()); } else { logger.error("No handling sender found for: {}", typeOfReceiver); } From afcec24d5e18a74a4923c9956cd25c6d0766520e Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Tue, 24 Jan 2023 12:01:52 +0100 Subject: [PATCH 09/28] feat(core): removed perun-notification.properties Properties set in perun-notification.properties are not used with the exception of notif.sendMessages which was moved to perun.properties. --- .../perun/core/api/CoreConfig.java | 9 +++++++ perun-base/src/main/resources/perun-base.xml | 2 ++ .../managers/PerunNotifEmailManagerImpl.java | 6 +---- .../notif/managers/SchedulingManagerImpl.java | 26 ++++-------------- .../src/main/resources/perun-notification.xml | 27 ------------------- .../metacentrum/perun/notif/AbstractTest.java | 6 +---- 6 files changed, 18 insertions(+), 58 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java index affd29b3d9..77a354ba4f 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java @@ -73,6 +73,7 @@ public void initBeansUtils() { private String userExtSourcesPersistent; private List allowedCorsDomains; private String pdfFontPath; + private boolean notifSendMessages; private String smtpHost; private int smtpPort; private boolean smtpAuth; @@ -598,6 +599,14 @@ public void setPdfFontPath(String pdfFontPath) { this.pdfFontPath = pdfFontPath; } + public boolean getNotifSendMessages() { + return notifSendMessages; + } + + public void setNotifSendMessages(boolean notifSendMessages) { + this.notifSendMessages = notifSendMessages; + } + public String getSmtpHost() { return smtpHost; } diff --git a/perun-base/src/main/resources/perun-base.xml b/perun-base/src/main/resources/perun-base.xml index 79a67698c0..8cd2abf3d1 100644 --- a/perun-base/src/main/resources/perun-base.xml +++ b/perun-base/src/main/resources/perun-base.xml @@ -56,6 +56,7 @@ + @@ -163,6 +164,7 @@ https://login.elixir-czech.org/idp/ cz.metacentrum.perun.core.impl.ExtSourceIdp + true localhost 25 false diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java index 6dd5644dd3..89f3e34e6d 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java @@ -17,9 +17,6 @@ @Service("perunNotifEmailManager") public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { - @Autowired - private Properties propertiesBean; - private boolean sendMessages; private static final Logger logger = LoggerFactory.getLogger(PerunNotifEmailManager.class); @@ -29,8 +26,7 @@ public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { @PostConstruct public void init() throws Exception { - String sendMessages_s = (String) propertiesBean.get("notif.sendMessages"); - this.sendMessages = sendMessages_s != null && sendMessages_s.equals("true"); + this.sendMessages = BeansUtils.getCoreConfig().getNotifSendMessages(); } @Override diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java index 41fd42e121..4a69b09264 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java @@ -8,11 +8,9 @@ import cz.metacentrum.perun.notif.entities.PerunNotifPoolMessage; import cz.metacentrum.perun.notif.utils.NotifUtils; import java.util.List; -import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; -import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -31,14 +29,8 @@ public class SchedulingManagerImpl { private static final Logger logger = LoggerFactory.getLogger(SchedulingManagerImpl.class); private PerunSession session; - private static volatile AtomicBoolean running = new AtomicBoolean(false); - private static volatile AtomicBoolean runningAllowed = new AtomicBoolean(true); - - @Autowired - private Properties propertiesBean; - - @Autowired - private DataSource dataSource; + private static final AtomicBoolean running = new AtomicBoolean(false); + private static final AtomicBoolean runningAllowed = new AtomicBoolean(true); @Autowired private PerunNotifPoolMessageManager perunNotifPoolMessageManager; @@ -55,8 +47,6 @@ public class SchedulingManagerImpl { @Autowired private PerunBl perun; - private final String consumerName = "notifications"; - @PostConstruct public void init() { session = NotifUtils.getPerunSession(perun); @@ -97,12 +87,10 @@ public void doNotification() { /** * Loads notif audit messages from db restart their processing. * Call processing of one perunAuditMessage for each gotten msg. - * - * @throws InternalErrorException */ private void processPerunNotifAuditMessages() throws Exception { - List oldAuditMessages = null; + List oldAuditMessages; try { oldAuditMessages = perunNotifAuditMessagesManager.getAll(); } catch (Exception ex) { @@ -119,9 +107,9 @@ private void processPerunNotifAuditMessages() throws Exception { /** * The method loads perun audit messages from the database and saves them as PerunNotifAudiMessages. */ - public void processPerunAuditMessages() throws Exception { + public void processPerunAuditMessages() { try { - List events = perun.getAuditMessagesManagerBl().pollConsumerEvents(session, consumerName); + List events = perun.getAuditMessagesManagerBl().pollConsumerEvents(session, "notifications"); for (AuditEvent event : events) { try { perunNotifAuditMessagesManager.saveMessageToPerunAuditerMessage(event.getMessage(), session); @@ -141,10 +129,6 @@ public void processPerunAuditMessages() throws Exception { * auditer message from db. To accomplish a success, the message have to be well-formed, so that the * object can be parsed, there have to be matching notifRegex in the db. If the message is recognized and * the matching regex is assigned to the template, PerunNotifPoolMessage is created. - * - * @param perunAuditMessage - * @param session - * @throws InternalErrorException */ private void processPerunNotifAuditMessage(PerunNotifAuditMessage perunAuditMessage, PerunSession session) throws Exception { diff --git a/perun-notification/src/main/resources/perun-notification.xml b/perun-notification/src/main/resources/perun-notification.xml index 10e7a7b3bb..5298beede2 100644 --- a/perun-notification/src/main/resources/perun-notification.xml +++ b/perun-notification/src/main/resources/perun-notification.xml @@ -42,31 +42,4 @@ http://www.springframework.org/schema/context http://www.springframework.org/sch - - - - - - file:@perun.conf@perun-notification.properties - file:${perun.conf.custom}perun-notification.properties - - - - - - - - - - - - perun@localhost - perun@localhost test message - true - notifications - - - - - diff --git a/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java b/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java index 11a9900c3b..344005ebbf 100644 --- a/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java +++ b/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java @@ -49,9 +49,6 @@ public class AbstractTest { @Autowired private ApplicationContext appContext; - @Autowired - Properties propertiesBean; - public static String convertStreamToString(java.io.InputStream is) { java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; @@ -67,8 +64,7 @@ public static void shutdown() { @Before public void startSmtpServer() { if (smtpServer == null) { - int port = (propertiesBean.getProperty("notif.port") != null) ? Integer.parseInt(propertiesBean.getProperty("notif.port")) : 8086; - smtpServer = SimpleSmtpServer.start(port); + smtpServer = SimpleSmtpServer.start(8086); } } From 37e82022e2a25c6ef6cc310bf4c8dc4b474fbcaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Wed, 25 Jan 2023 07:33:22 +0100 Subject: [PATCH 10/28] docs(core): add mfa_rules documentation to perun-roles file * added missing description of mfa_rules * resolves #3854 --- perun-base/src/main/resources/perun-roles.yml | 16 +++++++++++++++- perun-base/src/test/resources/test-roles.yml | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 6464fd8c92..166e0628ae 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -149,9 +149,10 @@ perun_roles: # optional word with a dash at the end can be used before the method name. # Example: filter-getAllMembers_Group_policy # -# Each policy is composed of two parts. +# Each policy is composed of two parts and one optional part. # The first one is called policy_roles, which contains privileged roles for this policy. # The second one is include_policies which contains policies which add their policy_roles to this policy. +# Another part, mfa_rules, is optional and is used for defining critical objects of the method. # # The policy_roles is a list of maps, where the relation between list entries is logical OR # and the relation between map entries is logical AND. @@ -185,6 +186,19 @@ perun_roles: # - default_policy # - getAllVos_policy # +# The mfa_rules is a list of rules marking critical objects of the method, which forces user to have valid Multi-Factor +# authentication. The objects themselves need to be marked as critical (in an attribute). Requiring MFA does not need +# to be related to critical objects, the method itself can be marked as critical. The elements of the list are related +# by OR relation meaning if any of the objects is critical, then the method call is considered critical. +# Example, creating subgroup in a group: +# mfa_rules: +# - MFA: Group +# - MFA: Vo +# If parent group or virtual organization is marked as critical (in an attribute), then MFA is required to call this +# method. If +# - MFA: +# would be used instead, the method would require MFA always. +# Some roles (usually system ones) can be exempted from having MFA to call critical operation. perun_policies: default_policy: diff --git a/perun-base/src/test/resources/test-roles.yml b/perun-base/src/test/resources/test-roles.yml index 4e47a67d55..151a2b1f83 100644 --- a/perun-base/src/test/resources/test-roles.yml +++ b/perun-base/src/test/resources/test-roles.yml @@ -10,9 +10,10 @@ perun_roles: [] # optional word with a dash at the end can be used before the method name. # Example: filter-getAllMembers_Group_policy # -# Each policy is composed of two parts. +# Each policy is composed of two parts and one optional part. # The first one is called policy_roles, which contains privileged roles for this policy. # The second one is include_policies which contains policies which add their policy_roles to this policy. +# Another part, mfa_rules, is optional and is used for defining critical objects of the method. # # The policy_roles is a list of maps, where the relation between list entries is logical OR # and the relation between map entries is logical AND. @@ -46,6 +47,19 @@ perun_roles: [] # - default_policy # - getAllVos_policy # +# The mfa_rules is a list of rules marking critical objects of the method, which forces user to have valid Multi-Factor +# authentication. The objects themselves need to be marked as critical (in an attribute). Requiring MFA does not need +# to be related to critical objects, the method itself can be marked as critical. The elements of the list are related +# by OR relation meaning if any of the objects is critical, then the method call is considered critical. +# Example, creating subgroup in a group: +# mfa_rules: +# - MFA: Group +# - MFA: Vo +# If parent group or virtual organization is marked as critical (in an attribute), then MFA is required to call this +# method. If +# - MFA: +# would be used instead, the method would require MFA always. +# Some roles (usually system ones) can be exempted from having MFA to call critical operation. perun_policies: default_policy: From 659e0900337e8d853f1038c53fe8ba4030409228 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:39:32 +0000 Subject: [PATCH 11/28] fix(deps): update dependency com.google.apis:google-api-services-admin-directory to directory_v1-rev20230124-2.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aab32deda7..4571171dac 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 1.6 0.5.10 9.1.22 - directory_v1-rev20230103-2.0.0 + directory_v1-rev20230124-2.0.0 2.2.21.Final 3.2.10.Final 1.1.0.GA From 71faa00288150b561fc00bd2c1cc6e3883bc7592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Thu, 26 Jan 2023 12:09:49 +0100 Subject: [PATCH 12/28] fix(core): fix loading roles from configuration * roles loaded from config were wrongly removed when user existed --- .../perun/core/blImpl/AuthzResolverBlImpl.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 2b528d9872..f59f329163 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -2494,20 +2494,22 @@ public static synchronized void refreshAuthz(PerunSession sess) { Utils.checkPerunSession(sess); log.trace("Refreshing authz roles for session {}.", sess); - //set empty set of roles + // Set empty set of roles sess.getPerunPrincipal().setRoles(new AuthzRoles()); - //Prepare service roles like engine, service, registrar, perunAdmin etc. + // Prepare service roles like engine, service, registrar, perunAdmin etc. boolean serviceRole = prepareServiceRoles(sess); - // no need to search further for service principals included in 'dontlookupusers' configuration + // No need to search further for service principals included in 'dontlookupusers' configuration if (!serviceRole || !BeansUtils.getCoreConfig().getDontLookupUsers().contains(sess.getPerunPrincipal().getActor())) { User user = sess.getPerunPrincipal().getUser(); - AuthzRoles roles; - if (user == null) { - roles = new AuthzRoles(); - } else { + AuthzRoles roles = sess.getPerunPrincipal().getRoles(); + if (user != null) { + AuthzRoles userRoles = authzResolverImpl.getRoles(user); + // Add service roles, they don't have complementary objects + roles.getRolesNames().forEach(userRoles::putAuthzRole); + roles = userRoles; // Load all user's roles with all possible subgroups - roles = addAllSubgroupsToAuthzRoles(sess, authzResolverImpl.getRoles(user), Role.GROUPADMIN); + roles = addAllSubgroupsToAuthzRoles(sess, roles, Role.GROUPADMIN); roles = addAllSubgroupsToAuthzRoles(sess, roles, Role.GROUPOBSERVER); roles = addAllSubgroupsToAuthzRoles(sess, roles, Role.GROUPMEMBERSHIPMANAGER); // Add self role for the user From b76336cd29de39c1d9d144e082cf70f627405244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Fri, 27 Jan 2023 09:16:51 +0100 Subject: [PATCH 13/28] refactor(core): removed module for user:virt:optionalLogin-namespace:mu - It was replaced with user:virt:optional-login-namespace:mu attribute. --- ...e_def_virt_optionalLogin_namespace_mu.java | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java deleted file mode 100644 index d62aeee66c..0000000000 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.core.impl.modules.attributes; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributeDefinition; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.ExtSourcesManager; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.impl.PerunSessionImpl; -import cz.metacentrum.perun.core.impl.Utils; -import cz.metacentrum.perun.core.implApi.modules.attributes.SkipValueCheckDuringDependencyCheck; -import cz.metacentrum.perun.core.implApi.modules.attributes.UserVirtualAttributesModuleAbstract; - -import java.util.Collections; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Attribute value depends on login-namespace:mu attribute - * Deprecated, use urn_perun_user_attribute_def_virt_optional_login_namespace_mu instead. - * - * @author Simona Kruppova, Michal Stava - */ -@Deprecated -@SkipValueCheckDuringDependencyCheck -public class urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu extends UserVirtualAttributesModuleAbstract { - - private final String EXTSOURCE_MUNI_IDP2 = "https://idp2.ics.muni.cz/idp/shibboleth"; - private static final Pattern loginMUPattern = Pattern.compile("^([0-9]+)[@]muni[.]cz$"); - - private static final String A_U_D_loginNamespace_mu = AttributesManager.NS_USER_ATTR_DEF + ":login-namespace:mu"; - - @Override - public Attribute getAttributeValue(PerunSessionImpl sess, User user, AttributeDefinition attributeDefinition) { - Attribute attribute = new Attribute(attributeDefinition); - - try { - Attribute loginInMU = sess.getPerunBl().getAttributesManagerBl().getAttribute(sess, user, A_U_D_loginNamespace_mu); - Utils.copyAttributeToVirtualAttributeWithValue(loginInMU, attribute); - } catch (AttributeNotExistsException ex) { - //That means that mu login attribute not exists at all - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } - - //if attribute is still null (empty login in mu or not existing attribute), try to find uco in user ext sources - if(attribute.getValue() == null) { - List userExtSources = sess.getPerunBl().getUsersManagerBl().getUserExtSources(sess, user); - for(UserExtSource userExtSource : userExtSources) { - ExtSource extSource = userExtSource.getExtSource(); - - //Skip if extSource is not the one we are looking for - if(userExtSource.getLogin() == null || extSource == null) continue; - if(!ExtSourcesManager.EXTSOURCE_IDP.equals(extSource.getType())) continue; - if(!EXTSOURCE_MUNI_IDP2.equals(extSource.getName())) continue; - - //Get login from this extSource and get only UCO from it - String login = userExtSource.getLogin(); - Matcher loginMUMatcher = loginMUPattern.matcher(login); - //This user has login in mu, but in weird format so skip this one - if(!loginMUMatcher.find()) continue; - //It is ok, take UCO from login and set it to attribute value - String UCO = loginMUMatcher.group(1); - attribute.setValue(UCO); - break; - } - } - - return attribute; - } - - @Override - public List getStrongDependencies() { - return Collections.singletonList(A_U_D_loginNamespace_mu); - } - - @Override - public AttributeDefinition getAttributeDefinition() { - AttributeDefinition attr = new AttributeDefinition(); - attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); - attr.setFriendlyName("optionalLogin-namespace:mu"); - attr.setDisplayName("MU login (if available)"); - attr.setType(String.class.getName()); - attr.setDescription("Masaryk University login (if available)"); - return attr; - } -} From c095e5c4634f874e3c245c3459decfed7c7f11dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Thu, 26 Jan 2023 16:00:54 +0100 Subject: [PATCH 14/28] fix(gui): fixed null check on user from session --- .../perun/webgui/client/WebGui.java | 6 +- .../webgui/client/mainmenu/MainMenu.java | 65 +++++++++---------- .../perun/webgui/tabs/TabManager.java | 1 + 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/WebGui.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/WebGui.java index 35b75320ee..ac1130ab8a 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/WebGui.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/WebGui.java @@ -206,7 +206,7 @@ public void onFinished(JavaScriptObject jso) { } // check if user exists - if (session.getUser().isServiceUser()) { + if (session.getUser() != null && session.getUser().isServiceUser()) { // if not and no role, redraw page body RootLayoutPanel body = RootLayoutPanel.get(); loadingBox.hide(); @@ -320,7 +320,9 @@ public void onClick(ClickEvent event) { // display logged user session.getUiElements().setLoggedUserInfo(pp); - session.getUiElements().setLogText("Welcome "+pp.getUser().getFullNameWithTitles()); + if (pp.getUser() != null) { + session.getUiElements().setLogText("Welcome " + pp.getUser().getFullNameWithTitles()); + } // show extended info ? boolean showExtendedInfo = false; diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/mainmenu/MainMenu.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/mainmenu/MainMenu.java index da4a0d96bb..1b62fdaff2 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/mainmenu/MainMenu.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/client/mainmenu/MainMenu.java @@ -64,25 +64,24 @@ public void onSelectionChange(SelectionChangeEvent event) { int menuPosition = menuStackPanel.getSelectedIndex(); for(int i = 0; i < 6; i++){ if(sectionsIds.containsKey(i)){ - if(sectionsIds.get(i) == menuPosition) - { - // THIS ENSURE ALL LINKS IN MENU ARE ALWAYS CURRENT WHEN MENU IS OPENED - updateHeaders(); - updateLinks(i); - - // IF NO TAB IS OPENED - OPEN ITS DEFAULT PAGE - if (session.getTabManager().getActiveTab() == null) { - // skip for perun admin menu - if (PERUN_ADMIN != i) { - MainMenuSection menuSec = sectionsMap.get(i); - if (menuSec != null) { - session.getTabManager().addTab(menuSec.getTabItem()); - } - } - } - - return; - } + if(sectionsIds.get(i) == menuPosition) { + // THIS ENSURES ALL LINKS IN MENU ARE ALWAYS CURRENT WHEN MENU IS OPENED + updateHeaders(); + updateLinks(i); + + // IF NO TAB IS OPENED - OPEN ITS DEFAULT PAGE + if (session.getTabManager().getActiveTab() == null) { + // skip for perun admin menu + if (PERUN_ADMIN != i) { + MainMenuSection menuSec = sectionsMap.get(i); + if (menuSec != null) { + session.getTabManager().addTab(menuSec.getTabItem()); + } + } + } + + return; + } } } } @@ -582,18 +581,16 @@ private void buildUserMenu() { if (session.getEditableSponsoredUsers().size() > 0) { sponsored = new SelfSponsoredUsersTabItem(user); } - } else { - detail = new IdentitySelectorTabItem(); } // display user changer for PerunAdmin or user with service identities if (session.isPerunAdmin() || session.getEditableUsers().size() > 1) { - changer = new IdentitySelectorTabItem(); + if (user != null) changer = new IdentitySelectorTabItem(); } menu.addItem(new MainMenuItem("Select identity", changer, SmallIcons.INSTANCE.userGrayIcon())); menu.addSplitter(); - menu.setTabItem(detail); + if (detail != null) menu.setTabItem(detail); menu.addItem(new MainMenuItem((user != null) ? user.getFullNameWithTitles() : "My profile", detail, SmallIcons.INSTANCE.userGrayIcon())); menu.addItem(new MainMenuItem("VO settings", settings, SmallIcons.INSTANCE.buildingIcon())); menu.addItem(new MainMenuItem("Resources settings", resources, SmallIcons.INSTANCE.settingToolsIcon())); @@ -601,17 +598,19 @@ private void buildUserMenu() { menu.addItem(new MainMenuItem("Publications", publications, SmallIcons.INSTANCE.booksIcon())); menu.addItem(new MainMenuItem("Applications", applications, SmallIcons.INSTANCE.applicationFromStorageIcon())); - if (user.isServiceUser()) { - menu.addItem(new MainMenuItem("Associated users", services, SmallIcons.INSTANCE.userRedIcon())); - if (user.isSponsoredUser()) { - menu.addItem(new MainMenuItem("Sponsors", sponsored, SmallIcons.INSTANCE.userGrayIcon())); - } - } else { - menu.addItem(new MainMenuItem("Service identities", services, SmallIcons.INSTANCE.userRedIcon())); - if (user.isSponsoredUser()) { - menu.addItem(new MainMenuItem("Sponsors", sponsored, SmallIcons.INSTANCE.userGrayIcon())); + if (user != null) { + if (user.isServiceUser()) { + menu.addItem(new MainMenuItem("Associated users", services, SmallIcons.INSTANCE.userRedIcon())); + if (user.isSponsoredUser()) { + menu.addItem(new MainMenuItem("Sponsors", sponsored, SmallIcons.INSTANCE.userGrayIcon())); + } } else { - menu.addItem(new MainMenuItem("Sponsored users", sponsored, SmallIcons.INSTANCE.userGrayIcon())); + menu.addItem(new MainMenuItem("Service identities", services, SmallIcons.INSTANCE.userRedIcon())); + if (user.isSponsoredUser()) { + menu.addItem(new MainMenuItem("Sponsors", sponsored, SmallIcons.INSTANCE.userGrayIcon())); + } else { + menu.addItem(new MainMenuItem("Sponsored users", sponsored, SmallIcons.INSTANCE.userGrayIcon())); + } } } diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/TabManager.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/TabManager.java index 46a92cb894..50de4238d6 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/TabManager.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/TabManager.java @@ -105,6 +105,7 @@ public static void closeOverlayTabFromJS(int selectedTabUniqueId) { * @return */ public boolean addTab(TabItem tab) { + if (tab == null) return false; return addTab(tab, true); } From 83a2c6bcdddbf049860a6e45484a08dd005ed0c1 Mon Sep 17 00:00:00 2001 From: David Flor <493294@mail.muni.cz> Date: Mon, 16 Jan 2023 11:34:39 +0100 Subject: [PATCH 15/28] feat(registrar): reject all group applications of user in vo after rejecting INITIAL vo application * Other group applications in the vo are automatically rejected when vo application is rejected, since those applications could not be used anyway * wrote test to cover changes made --- .../registrar/impl/RegistrarManagerImpl.java | 17 +++++++ .../AppAutoRejectionSchedulerTest.java | 49 ++++++++++++++----- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java index 2347dfa287..bd06af397d 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/RegistrarManagerImpl.java @@ -35,6 +35,7 @@ import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.blImpl.PerunBlImpl; import cz.metacentrum.perun.core.impl.Compatibility; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; import cz.metacentrum.perun.registrar.ConsolidatorManager; import cz.metacentrum.perun.registrar.exceptions.*; import cz.metacentrum.perun.registrar.model.RichApplication; @@ -186,6 +187,9 @@ public class RegistrarManagerImpl implements RegistrarManager { private static final String NAMESPACE_GROUP_HTML_MAIL_FOOTER = AttributesManager.NS_GROUP_ATTR_DEF; static final String URN_GROUP_HTML_MAIL_FOOTER = NAMESPACE_GROUP_HTML_MAIL_FOOTER + ":" + FRIENDLY_NAME_GROUP_HTML_MAIL_FOOTER; + private static final String DEFAULT_GROUP_MSG_VO = "Your application to group {groupName} was automatically rejected," + + " because the application for organization {voName} was rejected."; + private static final String MODULE_PACKAGE_PATH = "cz.metacentrum.perun.registrar.modules."; @Autowired PerunBl perun; @@ -1558,6 +1562,19 @@ public Application rejectApplication(PerunSession sess, int appId, String reason log.info("Application {} marked as REJECTED.", appId); deleteApplicationReservedLogins(sess, app); + // reject all of user's other group applications to the VO + if (app.getGroup() == null && app.getType().equals(AppType.INITIAL)) { + // create dummy principal and session using users information to pass it into getOpenApplicationsForUserInVo + PerunPrincipal userPrincipal = new PerunPrincipal(app.getCreatedBy(), "", "", app.getUser()); + userPrincipal.setExtSourceName(app.getExtSourceName()); + userPrincipal.setExtSourceType(app.getExtSourceType()); + userPrincipal.setAdditionalInformations(BeansUtils.stringToMapOfAttributes(app.getFedInfo())); + PerunSession helperSess = new PerunSessionImpl(sess.getPerun(), userPrincipal, sess.getPerunClient()); + List otherApps = registrarManager.getOpenApplicationsForUserInVo(helperSess, app.getVo()); + for (Application otherApp : otherApps) { + registrarManager.rejectApplication(sess, otherApp.getId(), DEFAULT_GROUP_MSG_VO); + } + } // log perun.getAuditer().log(sess, new ApplicationRejected(app)); diff --git a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java index 073a7a4ff1..6ab320c312 100644 --- a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java +++ b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java @@ -9,7 +9,6 @@ import cz.metacentrum.perun.core.api.Vo; import cz.metacentrum.perun.core.api.exceptions.GroupExistsException; import cz.metacentrum.perun.core.impl.Auditer; -import cz.metacentrum.perun.core.impl.Compatibility; import cz.metacentrum.perun.registrar.impl.AppAutoRejectionScheduler; import cz.metacentrum.perun.registrar.model.Application; import cz.metacentrum.perun.registrar.model.ApplicationForm; @@ -85,6 +84,7 @@ public void setUp() throws Exception { setUpVo(); setApplicationUser(); + ReflectionTestUtils.setField(spyScheduler.getPerun(), "auditer", auditerMock); } @@ -107,7 +107,7 @@ public void checkApplicationsExpirationForGroup() throws Exception { System.out.println(CLASS_NAME + "checkApplicationsExpirationForGroup"); // set up expired application and reject it - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); spyScheduler.checkApplicationsExpiration(); @@ -147,7 +147,7 @@ public void checkVoApplicationShouldNotBeAutoRejected() throws Exception { public void checkGroupApplicationShouldBeAutoRejected() throws Exception { System.out.println(CLASS_NAME + "checkGroupApplicationShouldBeAutoRejected"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -161,7 +161,7 @@ public void checkGroupApplicationShouldBeAutoRejected() throws Exception { public void checkGroupApplicationShouldNotBeAutoRejected() throws Exception { System.out.println(CLASS_NAME + "checkGroupApplicationShouldNotBeAutoRejected"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(50, group, GROUP_APP_EXP_RULES); @@ -172,6 +172,31 @@ public void checkGroupApplicationShouldNotBeAutoRejected() throws Exception { assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.VERIFIED); } + @Test + public void checkGroupApplicationsRejectedWhenVoApplicationRejected() throws Exception { + System.out.println(CLASS_NAME + "checkGroupApplicationsRejectedWhenVoApplicationRejected"); + + Group group1 = createGroup("Group1"); + Group group2 = createGroup("Group2"); + + Application expiredVoApp = setUpAndSubmitAppForPotentialAutoRejection(70, null, VO_APP_EXP_RULES); + + Application nonExpiredGroupApp = setUpAndSubmitAppForPotentialAutoRejection(50, group1, GROUP_APP_EXP_RULES); + Application expiredGroupApp = setUpAndSubmitAppForPotentialAutoRejection(70, group2, GROUP_APP_EXP_RULES); + + ReflectionTestUtils.invokeMethod(spyScheduler, "voApplicationsAutoRejection", Collections.singletonList(vo)); + + // check results + Application returnedApp = registrarManager.getApplicationById(session, expiredVoApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + + returnedApp = registrarManager.getApplicationById(session, nonExpiredGroupApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + + returnedApp = registrarManager.getApplicationById(session, expiredGroupApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + } + @Test public void voAdminIgnoredCustomMessage() throws Exception { System.out.println(CLASS_NAME + "voAdminIgnoredCustomMessage"); @@ -242,7 +267,7 @@ public void voEmailVerificationDefaultMessage() throws Exception { public void groupAdminIgnoredCustomMessage() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredCustomMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -260,7 +285,7 @@ public void groupAdminIgnoredCustomMessage() throws Exception { public void groupAdminIgnoredDefaultMessage() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredDefaultMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -276,7 +301,7 @@ public void groupAdminIgnoredDefaultMessage() throws Exception { public void groupEmailVerificationCustomMessage() throws Exception { System.out.println(CLASS_NAME + "groupEmailVerificationCustomMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -297,7 +322,7 @@ public void groupEmailVerificationCustomMessage() throws Exception { public void groupEmailVerificationDefaultMessage() throws Exception { System.out.println(CLASS_NAME + "groupEmailVerificationDefaultMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -398,7 +423,7 @@ public void voMailVerificationCustomMessageDefault() throws Exception { public void groupAdminIgnoredCustomMessageDefault() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredCustomMessageDefault"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -420,7 +445,7 @@ public void groupAdminIgnoredCustomMessageDefault() throws Exception { public void groupMailVerificationCustomMessageDefault() throws Exception { System.out.println(CLASS_NAME + "groupMailVerificationCustomMessageDefault"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -467,9 +492,9 @@ private void setApplicationUser() { * @return created group * @throws GroupExistsException if group already exists */ - private Group createGroup() throws GroupExistsException { + private Group createGroup(String name) throws GroupExistsException { Group group = new Group(); - group.setName("Group for apply"); + group.setName(name); return perun.getGroupsManagerBl().createGroup(session, vo, group); } From d2c4392d7af91a70e9ad71243490f5edc5413778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Mon, 30 Jan 2023 08:43:38 +0100 Subject: [PATCH 16/28] fix(core): allow account deletion in admin-meta --- .../impl/modules/pwdmgr/AdminmetaPasswordManagerModule.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/pwdmgr/AdminmetaPasswordManagerModule.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/pwdmgr/AdminmetaPasswordManagerModule.java index 6c52f63194..67ca1d8e51 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/pwdmgr/AdminmetaPasswordManagerModule.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/pwdmgr/AdminmetaPasswordManagerModule.java @@ -92,11 +92,6 @@ public void validatePassword(PerunSession sess, String userLogin, User user) thr super.validatePassword(sess, userLogin, user); } - @Override - public void deletePassword(PerunSession sess, String userLogin) { - throw new InternalErrorException("Deleting password in login namespace 'admin-meta' is not supported."); - } - @Override public void createAlternativePassword(PerunSession sess, User user, String passwordId, String password) throws PasswordStrengthException { throw new InternalErrorException("Creating alternative password in login namespace 'admin-meta' is not supported."); From a22bb5ebd747d7f0dcc975b5e99b6ee857eb1081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Tue, 31 Jan 2023 12:50:10 +0100 Subject: [PATCH 17/28] feat(core): add options to sort paginated members by email, organization and statuses * extended membersOrderColumn with email, organization and statuses, which will enable sorting in GUI by these parameters * preferences (order) of email and organization are taken from current GUI logic (mixing user and member attributes) * logins are not sortable as they are not unifiable --- .../perun/core/api/MembersOrderColumn.java | 34 ++++- .../perun/core/entry/MembersManagerEntry.java | 6 + .../perun/core/impl/MembersManagerImpl.java | 8 +- .../MembersManagerEntryIntegrationTest.java | 126 ++++++++++++++++++ perun-openapi/openapi.yml | 4 + 5 files changed, 175 insertions(+), 3 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java index d9804e3bca..2f916a291a 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java @@ -18,7 +18,39 @@ public enum MembersOrderColumn { "users.first_name " + getLangSql(query) + query.getOrder().getSqlValue() ), - ID("", "", query -> "members.id " + query.getOrder().getSqlValue()); + ID("", "", query -> "members.id " + query.getOrder().getSqlValue()), + STATUS("","", query -> "members.status " + query.getOrder().getSqlValue()), + GROUP_STATUS("", "", query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), + + // 1. user preferred mail, 2. member mail + EMAIL( + ", usrvals.attr_value, memvals.attr_value ", + " left join " + + "(select attr_value, member_id, attr_id from member_attr_values) as memvals " + + "on members.id=memvals.member_id and memvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:member:attribute-def:def:mail') " + + " left join " + + "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:preferredMail') ", + query -> "usrvals.attr_value " + query.getOrder().getSqlValue() + ", " + + "memvals.attr_value " + query.getOrder().getSqlValue() + ), + + // 1. member organization, 2. user organization (from IdP) + ORGANIZATION( + ", usrvals.attr_value, memvals.attr_value ", + " left join " + + "(select attr_value, member_id, attr_id from member_attr_values) as memvals " + + "on members.id=memvals.member_id and memvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:member:attribute-def:def:organization') " + + " left join " + + "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:organization') ", + query -> "memvals.attr_value " + query.getOrder().getSqlValue() + ", " + + "usrvals.attr_value " + query.getOrder().getSqlValue() + ); private final Function orderBySqlFunction; private final String selectSql; diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index bdff373ef8..22ba8a9b0e 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -12,6 +12,7 @@ import cz.metacentrum.perun.core.api.ExtSource; import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MemberWithSponsors; import cz.metacentrum.perun.core.api.MembersManager; import cz.metacentrum.perun.core.api.PerunSession; @@ -33,6 +34,7 @@ import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.GroupResourceMismatchException; +import cz.metacentrum.perun.core.api.exceptions.IllegalArgumentException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.api.exceptions.InvalidLoginException; import cz.metacentrum.perun.core.api.exceptions.InvalidSponsoredUserDataException; @@ -1650,6 +1652,10 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag } } + if (MembersOrderColumn.GROUP_STATUS.equals(query.getSortColumn()) && query.getGroupId() == null) { + throw new IllegalArgumentException("Group status cannot be used to sort VO members."); + } + Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames); if (query.getGroupId() == null) { diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java index 5c416f43c9..4695e919d0 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java @@ -856,11 +856,14 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { String voSelect = "SELECT " + memberMappingSelectQuery + " ,count(*) OVER() AS total_count" + - " FROM members JOIN users on members.user_id = users.id"; + query.getSortColumn().getSqlSelect() + + " FROM members JOIN users on members.user_id = users.id " + + query.getSortColumn().getSqlJoin(); String groupSelect = "SELECT " + groupsMembersMappingSelectQuery + " ,count(*) OVER() AS total_count" + + query.getSortColumn().getSqlSelect() + " FROM" + " (SELECT group_id, member_id, min(source_group_status) as source_group_status," + " min(membership_type) as membership_type, null as source_group_id" + @@ -868,7 +871,8 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { " WHERE group_id = (:groupId)" + " GROUP BY group_id, member_id) groups_members" + " LEFT JOIN members on groups_members.member_id = members.id" + - " LEFT JOIN users on members.user_id = users.id"; + " LEFT JOIN users on members.user_id = users.id " + + query.getSortColumn().getSqlJoin(); return query.getGroupId() == null ? voSelect : groupSelect; } diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java index c44a0e8e7a..c2ff1dff3e 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java @@ -2879,6 +2879,126 @@ public void getMembersPageIdSortWorks() throws Exception { .containsExactly(member1.getId(), member2.getId(), member3.getId()); } + @Test + public void getMembersPageSortsByEmail() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByEmail"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + AttributeDefinition mailAttrDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, A_M_MAIL); + AttributeDefinition prefMailAttrDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, A_U_PREFERRED_MAIL); + + Attribute memberMail = new Attribute(mailAttrDef); + Attribute prefMail = new Attribute(prefMailAttrDef); + + Member member1 = setUpMember(vo, "Doe", "John"); + memberMail.setValue("mail@mail.com"); + perun.getAttributesManagerBl().setAttribute(sess, member1, memberMail); + + Member member2 = setUpMember(vo, "Do", "Jo"); + memberMail.setValue("nail@nail.com"); + perun.getAttributesManagerBl().setAttribute(sess, member2, memberMail); + + Member member3 = setUpMember(vo, "Nom Ail", "Madelin"); + + Member member4 = setUpMember(vo, "Don", "Joe"); + prefMail.setValue("ali@k.com"); + memberMail.setValue("zli@k.com"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member4), prefMail); + perun.getAttributesManagerBl().setAttribute(sess, member4, memberMail); + + Member member5 = setUpMember(vo, "Doney", "Joey"); + prefMail.setValue("Don@ey.com"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member5), prefMail); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.EMAIL); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of(prefMail.getName(), memberMail.getName())); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) // alik, doney, mail, nail, NULL + .containsExactly(member4.getId(), member5.getId(), member1.getId(), member2.getId(), member3.getId()); + } + + @Test + public void getMembersPageSortsByOrganization() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByOrganization"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + AttributeDefinition memberOrgDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, AttributesManager.NS_MEMBER_ATTR_DEF + ":organization"); + AttributeDefinition userOrgDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, AttributesManager.NS_USER_ATTR_DEF + ":organization"); + + Attribute memberOrg = new Attribute(memberOrgDef); + Attribute userOrg = new Attribute(userOrgDef); + + Member member3 = setUpMember(vo, "Don", "Joe"); + userOrg.setValue("eleland"); + memberOrg.setValue("Zazaland"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member3), userOrg); + perun.getAttributesManagerBl().setAttribute(sess, member3, memberOrg); + + Member member1 = setUpMember(vo, "Doe", "John"); + memberOrg.setValue("Babaland"); + perun.getAttributesManagerBl().setAttribute(sess, member1, memberOrg); + + Member member4 = setUpMember(vo, "Doney", "Joey"); + userOrg.setValue("gagaland"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member4), userOrg); + + Member member5 = setUpMember(vo, "Nom Ail", "Madelin"); + + Member member2 = setUpMember(vo, "Do", "Jo"); + memberOrg.setValue("Dadaland"); + perun.getAttributesManagerBl().setAttribute(sess, member2, memberOrg); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.ORGANIZATION); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of(userOrg.getName(), memberOrg.getName())); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) + .containsExactly(member1.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageSortsByStatus() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByStatus"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + Member member1 = setUpMember(vo, "Doe", "John"); + perun.getMembersManagerBl().validateMember(sess, member1); + perun.getMembersManagerBl().setStatus(sess, member1, Status.getStatus(4)); + + Member member2 = setUpMember(vo, "Do", "Jo"); + perun.getMembersManagerBl().validateMember(sess, member2); + perun.getMembersManagerBl().setStatus(sess, member2, Status.getStatus(3)); + + Member member3 = setUpMember(vo, "Do", "Jo"); + perun.getMembersManagerBl().setStatus(sess, member3, Status.getStatus(0)); + + Member member4 = setUpMember(vo, "Don", "Joe"); + perun.getMembersManagerBl().setStatus(sess, member4, Status.getStatus(1)); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.STATUS); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of()); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) + .containsExactly(member3.getId(), member4.getId(), member2.getId(), member1.getId()); + } + @Test public void getMembersPageIdSortDescendingWorks() throws Exception { System.out.println(CLASS_NAME + "getMembersPageIdSortDescendingWorks"); @@ -3159,6 +3279,12 @@ public void getGroupMembersPageOnDifferentSubgroupStatuses() throws Exception { assertThat(returnedMemberIds).containsExactly(member2.getId()); assertThat(result.getData().get(0).getMembershipType()).isEqualTo(MembershipType.DIRECT); + + query.setSortColumn(MembersOrderColumn.GROUP_STATUS); + query.setGroupStatuses(List.of()); + result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of()); + + assertThat(result.getData().get(2).getId()).isEqualTo(member2.getId()); } @Test diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 0d7c6db7b2..8579cf385f 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -227,6 +227,10 @@ components: enum: - ID - NAME + - EMAIL + - GROUP_STATUS + - STATUS + - ORGANIZATION MemberGroupStatus: type: string From 349fbb7d083bc2d89f1dd7b6c384efe64dc6d29a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:28:15 +0000 Subject: [PATCH 18/28] chore(deps): update dependency org.openapitools:openapi-generator-maven-plugin to v6.3.0 --- perun-openapi/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perun-openapi/pom.xml b/perun-openapi/pom.xml index 7ca042b9c1..94816fba62 100644 --- a/perun-openapi/pom.xml +++ b/perun-openapi/pom.xml @@ -28,7 +28,7 @@ org.openapitools openapi-generator-maven-plugin - 6.2.1 + 6.3.0 From 4d2cefa0b9f9222930c2970ad669374399e613d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Thu, 2 Feb 2023 07:51:39 +0100 Subject: [PATCH 19/28] fix(openapi): make group optional in invitationFormExists --- perun-openapi/openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 0d7c6db7b2..34ec107840 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -16838,7 +16838,7 @@ paths: summary: Checks if invitation form exists. parameters: - $ref: '#/components/parameters/voId' - - $ref: '#/components/parameters/groupId' + - $ref: '#/components/parameters/optionalGroupId' responses: '200': $ref: '#/components/responses/BooleanResponse' From ac1766e8d0bfbbd1bf50d39fd1bb75641554a4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johana=20Sup=C3=ADkov=C3=A1?= Date: Mon, 6 Feb 2023 07:14:41 +0100 Subject: [PATCH 20/28] fix(core): extend app view config attribute privilege * group managers may want to inherit VO configuration --- .../metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java index 25bbfbbc68..5cc12d4263 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java @@ -7814,6 +7814,8 @@ protected void initialize() { policies = new ArrayList<>(); policies.add(Triple.of(Role.VOADMIN, READ, RoleObject.Vo)); policies.add(Triple.of(Role.VOADMIN, WRITE, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPADMIN, READ, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPMEMBERSHIPMANAGER, READ, RoleObject.Vo)); attributes.put(attr, createInitialPolicyCollections(policies)); //urn:perun:group:attribute-def:def:groupStructureResources From 847357a464c96a995133b5f1039b39571c81cc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Wed, 8 Feb 2023 08:44:21 +0100 Subject: [PATCH 21/28] refactor(core): removed unused registration module CvutFbmi --- .../perun/registrar/modules/CvutFbmi.java | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java deleted file mode 100644 index 84ae2b815d..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.Group; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * This module sorts new VO members into the groups based on their affiliations from ČVUT/UK IdPs - * - * @author Pavel Zlámal - */ -public class CvutFbmi extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(CvutFbmi.class); - - /** - * Add approved VO members into specific groups based on their affiliation from ČVUT or UK IdP - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws MemberNotExistsException, WrongAttributeAssignmentException, AttributeNotExistsException, GroupNotExistsException { - - PerunBl perun = (PerunBl)session.getPerun(); - User user = app.getUser(); - Vo vo = app.getVo(); - - // For INITIAL VO APPLICATIONS - if (Application.AppType.INITIAL.equals(app.getType()) && app.getGroup() == null) { - - Attribute a = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_VIRT + ":eduPersonScopedAffiliations"); - - if (a.getValue() != null) { - - Member member = perun.getMembersManagerBl().getMemberByUser(session, vo, user); - - List affiliations = a.valueAsList(); - - if (affiliations.contains("employee@cvut.cz")) { - categorizeMember(session, vo, member, "Osoby:CVUT:Zamestnanec"); - } - if (affiliations.contains("student@cvut.cz")) { - categorizeMember(session, vo, member, "Osoby:CVUT:Student"); - } - if (affiliations.contains("employee@cuni.cz")) { - categorizeMember(session, vo, member, "Osoby:UK:Zamestnanec"); - } - if (affiliations.contains("student@cuni.cz")) { - categorizeMember(session, vo, member, "Osoby:UK:Student"); - } - - } - - } - - return app; - - } - - private void categorizeMember(PerunSession session, Vo vo, Member member, String groupName) { - - PerunBl perun = (PerunBl)session.getPerun(); - Group group = null; - try { - group = perun.getGroupsManagerBl().getGroupByName(session, vo, groupName); - perun.getGroupsManagerBl().addMember(session, group, member); - } catch (GroupNotExistsException e) { - log.warn("Destination group {} for ČVUT FBMI and UK members categorisation doesn't exists.", groupName, e); - } catch (WrongReferenceAttributeValueException | WrongAttributeValueException e) { - log.error("Member {} couldn't be added to expected group {}.", member, group, e); - } catch (AlreadyMemberException e) { - // IGNORE - } - - } - -} From 50593d35b2a13930d15e921c4f9da5037db596e0 Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Mon, 6 Feb 2023 11:54:38 +0100 Subject: [PATCH 22/28] fix(core): unique constraint to vos shortName Dropped unique constraint to name and added constraint to shortName instead. BREAKING CHANGE: updated version of DB --- perun-base/src/test/resources/test-schema.sql | 6 +++--- perun-core/src/main/resources/postgresChangelog.txt | 4 ++++ perun-db/postgres.sql | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 1d7f2fd1ee..5537d43095 100644 --- a/perun-base/src/test/resources/test-schema.sql +++ b/perun-base/src/test/resources/test-schema.sql @@ -1,4 +1,4 @@ --- database version 3.2.10 (don't forget to update insert statement at the end of file) +-- database version 3.2.11 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -14,7 +14,7 @@ create table vos ( created_by_uid integer, modified_by_uid integer, constraint vo_pk primary key (id), - constraint vo_u unique (name) + constraint vo_u unique (short_name) ); -- USERS - information about user as real person @@ -1881,7 +1881,7 @@ create index idx_fk_attr_critops ON attribute_critical_actions(attr_id); create index app_state_idx ON application (state); -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.10'); +insert into configurations values ('DATABASE VERSION','3.2.11'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); insert into membership_types (id, membership_type, description) values (2, 'INDIRECT', 'Member is added indirectly through UNION relation'); diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 4bd923f365..5221f39926 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -6,6 +6,10 @@ -- Directly under version number should be version commands. They will be executed in the order they are written here. -- Comments are prefixed with -- and can be written only between version blocks, that means not in the lines with commands. They have to be at the start of the line. +3.2.11 +ALTER TABLE vos DROP constraint vo_u; +ALTER TABLE vos ADD CONSTRAINT vo_u unique (short_name); + 3.2.10 ALTER TABLE resources_bans ALTER COLUMN banned_to SET DEFAULT '2999-01-01 00:00:00'; ALTER TABLE facilities_bans ALTER COLUMN banned_to SET DEFAULT '2999-01-01 00:00:00'; diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index 2ec9748b13..ffee74c992 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.10 (don't forget to update insert statement at the end of file) +-- database version 3.2.11 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -12,7 +12,7 @@ create table vos ( created_by_uid integer, modified_by_uid integer, constraint vo_pk primary key (id), - constraint vo_u unique (name) + constraint vo_u unique (short_name) ); -- USERS - information about user as real person @@ -1988,7 +1988,7 @@ grant all on attribute_critical_actions to perun; grant all on app_notifications_sent to perun; -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.10'); +insert into configurations values ('DATABASE VERSION','3.2.11'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); From 7cd86c13e2c4928cc2e288dbad19b501ce1e9d70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 01:31:47 +0000 Subject: [PATCH 23/28] chore(deps): update dependency org.codehaus.cargo:cargo-maven3-plugin to v1.10.5 --- perun-rpc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perun-rpc/pom.xml b/perun-rpc/pom.xml index 12c076e59f..0dd7fa5e77 100644 --- a/perun-rpc/pom.xml +++ b/perun-rpc/pom.xml @@ -34,7 +34,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.4 + 1.10.5 tomcat9x From b2b3a7dffc22117cd2d51230c43e79dfdab4447f Mon Sep 17 00:00:00 2001 From: Jan Pavlicek <469355@mail.muni.cz> Date: Thu, 9 Feb 2023 17:15:22 +0100 Subject: [PATCH 24/28] fix(openapi): typo causing wrong generation of RegistrarManager and MembersManager --- perun-openapi/openapi.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 46644c4669..da156a71f3 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -10936,7 +10936,7 @@ paths: /json/membersManager/addMemberCandidates: post: tags: - - membersManager + - MembersManager operationId: addMemberCandidates summary: Adds member candidates. responses: @@ -16808,7 +16808,7 @@ paths: /json/registrarManager/inviteMemberCandidates: post: tags: - - registrarManager + - RegistrarManager operationId: inviteMemberCandidates summary: Invite member candidates. responses: @@ -16837,7 +16837,7 @@ paths: /json/registrarManager/invitationFormExists: get: tags: - - registrarManager + - RegistrarManager operationId: invitationFormExists summary: Checks if invitation form exists. parameters: From 14c519c6711a395d297fba1f71c0aa6f58085a42 Mon Sep 17 00:00:00 2001 From: xflord <493294@mail.muni.cz> Date: Fri, 10 Feb 2023 11:19:26 +0100 Subject: [PATCH 25/28] fix(core): missing config version update in changelog --- perun-core/src/main/resources/postgresChangelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 5221f39926..4c5c1c54db 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -9,6 +9,7 @@ 3.2.11 ALTER TABLE vos DROP constraint vo_u; ALTER TABLE vos ADD CONSTRAINT vo_u unique (short_name); +UPDATE configurations set value='3.2.11' WHERE property='DATABASE VERSION'; 3.2.10 ALTER TABLE resources_bans ALTER COLUMN banned_to SET DEFAULT '2999-01-01 00:00:00'; From 83f56f62164426ef8fc139322d0c88b588d1add1 Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Fri, 10 Feb 2023 18:59:12 +0100 Subject: [PATCH 26/28] feat(python-cli): new commands getFacilitiesByAttribute and getUserAttributes new commands getFacilitiesByAttribute, getFacilitiesByAttributeWA and getUserAttributes --- .../perun/cli/getFacilitiesByAttribute.py | 40 ++++++++++++++ .../perun/cli/getFacilitiesByAttributeWA.py | 53 +++++++++++++++++++ perun-cli-python/perun/cli/getRichMember.py | 36 +++++++++++-- .../perun/cli/getUserAttributes.py | 40 ++++++++++++++ perun-cli-python/perun_cli.py | 6 +++ perun-openapi/openapi.yml | 2 + 6 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 perun-cli-python/perun/cli/getFacilitiesByAttribute.py create mode 100644 perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py create mode 100644 perun-cli-python/perun/cli/getUserAttributes.py diff --git a/perun-cli-python/perun/cli/getFacilitiesByAttribute.py b/perun-cli-python/perun/cli/getFacilitiesByAttribute.py new file mode 100644 index 0000000000..bbc7beb3c2 --- /dev/null +++ b/perun-cli-python/perun/cli/getFacilitiesByAttribute.py @@ -0,0 +1,40 @@ +from typing import Optional +from typer import Option +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import perun.cli +import typer + +from perun_openapi.model.facility import Facility + + +def main(attr_name: str = Option(..., '-a', '--attributeName', help='attribute name (namespace + : + friendlyName)'), + attr_value: str = Option(..., '-v', '--attributeValue', help='short name of VO'), + sort_by_id: bool = typer.Option(False, '-i', '--orderById', help='order by id'), + sort_by_name: bool = typer.Option(False, '-n', '--orderByName', help='order by short name') + ) -> None: + """ search for facilities by attributeName and attributeValue """ + rpc = perun.cli.rpc + try: + facilities: list[Facility] = rpc.facilities_manager.get_facilities_by_attribute(attr_name, attr_value) + if sort_by_id: + facilities.sort(key=lambda x: x.id) + if sort_by_name: + facilities.sort(key=lambda x: x.name) + console = Console() + # print user + table = Table(title="facilities") + table.add_column("id", justify="right") + table.add_column("name") + table.add_column("description") + for facility in facilities: + table.add_row(str(facility.id), str(facility.name), str(facility.description)) + console.print(table) + + except ApiException as ex: + print('error name:', PerunException(ex).name) + print('error message:', PerunException(ex).message) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py new file mode 100644 index 0000000000..86d18bff22 --- /dev/null +++ b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py @@ -0,0 +1,53 @@ +from typing import Optional +from typer import Option +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import perun.cli +import typer + +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.facility import Facility +from perun_openapi.model.facility_with_attributes import FacilityWithAttributes + + +def main( + attr_name: str = Option('urn:perun:facility:attribute-def:def:administratorContact', '-a', '--attributeName', help='attribute name (namespace + : + friendlyName)'), + attr_value: str = Option('martinkuba@gmail.com', '-v', '--attributeValue', help='short name of VO'), + attr_names: str = typer.Option('urn:perun:facility:attribute-def:def:administratorContact', '-r', '--returnedAttributeNames', help='names of returned attributes'), + ) -> None: + """ facilities with attributes """ + rpc = perun.cli.rpc + attr_names = attr_names.split(',') + try: + console = Console() + facilities: list[Facility] = rpc.facilities_manager.get_facilities_by_attribute(attr_name, attr_value) + table = Table(title="facilities") + table.add_column("id", justify="right") + table.add_column("name") + table.add_column("description") + for facility in facilities: + table.add_row(str(facility.id), str(facility.name), str(facility.description)) + console.print(table) + + for facility in facilities: + facility_attributes: list[Attribute] = perun.cli.rpc.attributes_manager.get_facility_attributes_by_names(facility.id, attr_names) + table = Table(title="facility " + facility.name + " attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in facility_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + + # does not work :-( + # facilitiesWA: list[FacilityWithAttributes] \ + # = rpc.facilities_manager.get_facilities_by_attribute_with_attributes(attr_name, attr_value, attr_names) + + except ApiException as ex: + print('error name:', PerunException(ex).name) + print('error message:', PerunException(ex).message) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun/cli/getRichMember.py b/perun-cli-python/perun/cli/getRichMember.py index b36cdd91cf..1358674cc7 100644 --- a/perun-cli-python/perun/cli/getRichMember.py +++ b/perun-cli-python/perun/cli/getRichMember.py @@ -6,16 +6,24 @@ import perun.cli import typer +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.member import Member +from perun_openapi.model.rich_member import RichMember +from perun_openapi.model.user import User +from perun_openapi.model.vo import Vo + def get_rich_member(user_id: int = typer.Option(3197, '-u', '--user_id', help='user ID'), vo_name: str = typer.Option("meta", '-v', '--voShortName', help='short name of VO')) -> None: """ tests getting complex object of RichMember """ rpc = perun.cli.rpc try: - user = rpc.users_manager.get_user_by_id(user_id) - vo = rpc.vos_manager.get_vo_by_short_name(vo_name) - member = rpc.members_manager.get_member_by_user(vo=vo.id, user=user.id) - rich_member = rpc.members_manager.get_rich_member(member.id) + user: User = rpc.users_manager.get_user_by_id(user_id) + vo: Vo = rpc.vos_manager.get_vo_by_short_name(vo_name) + member: Member = rpc.members_manager.get_member_by_user(vo=vo.id, user=user.id) + rich_member: RichMember = rpc.members_manager.get_rich_member_with_attributes(member.id) + user_attributes: list[Attribute] = rich_member.user_attributes + member_attributes: list[Attribute] = rich_member.member_attributes console = Console() # print user table = Table(title="user") @@ -43,6 +51,26 @@ def get_rich_member(user_id: int = typer.Option(3197, '-u', '--user_id', help='u for ues in rich_member.user_ext_sources: table.add_row(str(ues.id), ues.last_access, ues.login, ues.ext_source.name) console.print(table) + # print member attributes + if member_attributes: + table = Table(title="member attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in member_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + # print user attributes + if user_attributes: + table = Table(title="user attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in user_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) except ApiException as ex: print('error name:', PerunException(ex).name) print('error message:', PerunException(ex).message) diff --git a/perun-cli-python/perun/cli/getUserAttributes.py b/perun-cli-python/perun/cli/getUserAttributes.py new file mode 100644 index 0000000000..eb918480f9 --- /dev/null +++ b/perun-cli-python/perun/cli/getUserAttributes.py @@ -0,0 +1,40 @@ +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import typer +import perun.cli +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.rich_user import RichUser +from perun_openapi.model.user import User + + +def main(user_id: int = typer.Option(3197, '-u', '--user_id', help='user ID')) -> None: + """ prints user for a given id""" + try: + console: Console = Console() + # print user + user: User = perun.cli.rpc.users_manager.get_user_by_id(user_id) + table: Table = Table(title="user") + table.add_column("id", justify="right") + table.add_column("first_name") + table.add_column("last_name") + table.add_column("createdAt") + table.add_row(str(user.id), user.first_name, user.last_name, str(user.createdAt)) + console.print(table) + # print user attributes + user_attributes: list[Attribute] = perun.cli.rpc.attributes_manager.get_user_attributes(user_id) + if user_attributes: + table = Table(title="user attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in user_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + + except ApiException as ex: + print('error:', PerunException(ex).name) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun_cli.py b/perun-cli-python/perun_cli.py index 4a8ef55f22..864202db68 100755 --- a/perun-cli-python/perun_cli.py +++ b/perun-cli-python/perun_cli.py @@ -3,11 +3,14 @@ from perun_openapi.configuration import Configuration from perun.oidc import DeviceCodeOAuth, PerunInstance from perun.rpc import PerunRpc +import perun.cli.getFacilitiesByAttribute +import perun.cli.getFacilitiesByAttributeWA import perun.cli.getGroupMembers import perun.cli.getPerunPrincipal import perun.cli.getPerunStatus import perun.cli.getRichMember import perun.cli.getUser +import perun.cli.getUserAttributes import perun.cli.listOfMyVos import perun.cli.listOfUserRoles import perun.cli.listOfVos @@ -15,11 +18,14 @@ # see https://typer.tiangolo.com/tutorial/ app = typer.Typer(add_completion=False) +app.command(name="getFacilitiesByAttribute")(perun.cli.getFacilitiesByAttribute.main) +app.command(name="getFacilitiesByAttributeWA")(perun.cli.getFacilitiesByAttributeWA.main) app.command(name="getGroupMembers")(perun.cli.getGroupMembers.get_group_members) app.command(name="getPerunPrincipal")(perun.cli.getPerunPrincipal.main) app.command(name="getPerunStatus")(perun.cli.getPerunStatus.main) app.command(name="getRichMember")(perun.cli.getRichMember.get_rich_member) app.command(name="getUser")(perun.cli.getUser.get_user) +app.command(name="getUserAttributes")(perun.cli.getUserAttributes.main) app.command(name="listOfMyVos")(perun.cli.listOfMyVos.main) app.command(name="listOfUserRoles")(perun.cli.listOfUserRoles.main) app.command(name="listOfVos")(perun.cli.listOfVos.main) diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index da156a71f3..393c008c5a 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -506,6 +506,8 @@ components: attributes: { type: array, items: {$ref: '#/components/schemas/Attribute'} } required: - attributes + discriminator: + propertyName: beanName Host: allOf: From 76dcc7ef48b100f94e7042bf08003c8520f2a29c Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Fri, 10 Feb 2023 20:35:22 +0100 Subject: [PATCH 27/28] fix(openapi): fixed declaration of FacilityWithAttributes FacilityWithAttributes was wrongly declared as inheriting from Facility, it contains property facility instead --- .../cz/metacentrum/perun/cli/PerunCLI.java | 2 +- .../GetFacilityByAttributeWithAttributes.java | 46 +++++++++++++++++++ perun-cli-python/generate.sh | 2 +- .../perun/cli/getFacilitiesByAttributeWA.py | 16 +++---- perun-openapi/openapi.yml | 9 ++-- 5 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java diff --git a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java index 6220af1a68..169b30a829 100644 --- a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java +++ b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java @@ -121,7 +121,7 @@ private static void call(PerunCommand command, String[] cliArgs) throws ParseExc if (commandLine.hasOption(PERUN_URL_OPTION)) { perunUrl = commandLine.getOptionValue(PERUN_URL_OPTION); } - if (perunUrl == null) perunUrl = "https://perun.cesnet.cz/krb/rpc"; + if (perunUrl == null) perunUrl = "https://perun-api.e-infra.cz/krb/rpc"; // find user and password String user = System.getenv(PERUN_USER_VARIABLE); diff --git a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java new file mode 100644 index 0000000000..96bdc3d7f7 --- /dev/null +++ b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java @@ -0,0 +1,46 @@ +package cz.metacentrum.perun.cli.commands; + +import cz.metacentrum.perun.cli.PerunCLI; +import cz.metacentrum.perun.cli.PerunCommand; +import cz.metacentrum.perun.openapi.model.FacilityWithAttributes; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +import java.util.Arrays; +import java.util.List; + +/** + * Prints owners of facilities having the specified destination. + * + * @author Martin Kuba makub@ics.muni.cz + */ +@SuppressWarnings("unused") +public class GetFacilityByAttributeWithAttributes extends PerunCommand { + + @Override + public String getCommandDescription() { + return "prints attributes of facilities found by attribute value"; + } + + @Override + public void addOptions(Options options) { + options.addOption(Option.builder("a").required(true).hasArg(true).longOpt("attrName").desc("attribute name").build()); + options.addOption(Option.builder("v").required(true).hasArg(true).longOpt("attrValue").desc("attribute value").build()); + options.addOption(Option.builder("r").required(true).hasArg(true).longOpt("returnedAttributeNames").desc("names of returned attributes").build()); + } + + @Override + public void executeCommand(PerunCLI.CommandContext ctx) { + String attributeName = ctx.getCommandLine().getOptionValue("a"); + String attributeValue = ctx.getCommandLine().getOptionValue("v"); + List attrNames = Arrays.asList(ctx.getCommandLine().getOptionValue("r").split(",")); + + List facilities = ctx.getPerunRPC().getFacilitiesManager().getFacilitiesByAttributeWithAttributes(attributeName, attributeValue, attrNames); + + for (FacilityWithAttributes facility : facilities) { + System.out.println(facility); + } + + } + +} diff --git a/perun-cli-python/generate.sh b/perun-cli-python/generate.sh index b18f94dd16..c3f0ae5a02 100755 --- a/perun-cli-python/generate.sh +++ b/perun-cli-python/generate.sh @@ -1,6 +1,6 @@ #!/bin/bash -GENERATOR_VERSION=6.2.1 +GENERATOR_VERSION=6.3.0 if [ ! -f "openapi-generator-cli-$GENERATOR_VERSION.jar" ] ; then wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/$GENERATOR_VERSION/openapi-generator-cli-$GENERATOR_VERSION.jar fi diff --git a/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py index 86d18bff22..b37790cf3c 100644 --- a/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py +++ b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py @@ -23,18 +23,19 @@ def main( attr_names = attr_names.split(',') try: console = Console() - facilities: list[Facility] = rpc.facilities_manager.get_facilities_by_attribute(attr_name, attr_value) + facilitiesWithAttributes: list[FacilityWithAttributes] \ + = rpc.facilities_manager.get_facilities_by_attribute_with_attributes(attr_name, attr_value, attr_names) table = Table(title="facilities") table.add_column("id", justify="right") table.add_column("name") table.add_column("description") - for facility in facilities: - table.add_row(str(facility.id), str(facility.name), str(facility.description)) + for fwa in facilitiesWithAttributes: + table.add_row(str(fwa.facility.id), fwa.facility.name, fwa.facility.description) console.print(table) - for facility in facilities: - facility_attributes: list[Attribute] = perun.cli.rpc.attributes_manager.get_facility_attributes_by_names(facility.id, attr_names) - table = Table(title="facility " + facility.name + " attributes") + for fwa in facilitiesWithAttributes: + facility_attributes: list[Attribute] = fwa.attributes + table = Table(title="facility " + fwa.facility.name + " attributes") table.add_column("namespace") table.add_column("friendlyName") table.add_column("value") @@ -44,8 +45,7 @@ def main( console.print(table) # does not work :-( - # facilitiesWA: list[FacilityWithAttributes] \ - # = rpc.facilities_manager.get_facilities_by_attribute_with_attributes(attr_name, attr_value, attr_names) + except ApiException as ex: print('error name:', PerunException(ex).name) diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 393c008c5a..70d350870a 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -500,14 +500,13 @@ components: propertyName: beanName FacilityWithAttributes: - allOf: - - $ref: '#/components/schemas/Facility' + type: object properties: + facility: { $ref: '#/components/schemas/Facility' } attributes: { type: array, items: {$ref: '#/components/schemas/Attribute'} } required: + - facility - attributes - discriminator: - propertyName: beanName Host: allOf: @@ -2762,7 +2761,7 @@ components: required: true attrNames: - name: "attrNames[]" + name: "attrNames" description: "list of attribute names List" in: query required: true From adfeeca2f4627169d1d74d055964d29cb5a5f6c8 Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Wed, 25 Jan 2023 09:58:29 +0100 Subject: [PATCH 28/28] fix(registrar)!: notify admins if application auto-approval fails - send notification to the relevant vo/group admins - store error that caused approval failer in db - add new mail tag for error --- .../perun/registrar/model/Application.java | 14 +++-- perun-base/src/test/resources/test-schema.sql | 1 + perun-cli/Perun/beans/Application.pm | 20 +++++-- .../src/main/resources/postgresChangelog.txt | 1 + perun-db/postgres.sql | 1 + perun-openapi/openapi.yml | 1 + .../perun/registrar/impl/MailManagerImpl.java | 11 ++++ .../registrar/impl/RegistrarManagerImpl.java | 54 ++++++++++--------- .../parseRpcMethods.pl | 4 +- .../perun/webgui/model/Application.java | 18 +++++++ .../tabs/registrartabs/CreateMailTabItem.java | 1 + .../tabs/registrartabs/EditMailTabItem.java | 1 + 12 files changed, 93 insertions(+), 34 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java index 7cd05da749..7649136cf0 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java @@ -25,7 +25,7 @@ public static enum AppType { INITIAL, EXTENSION, EMBEDDED } private String extSourceType; private int extSourceLoa = 0; // 0 - by default private User user; - + private String autoApproveError; private String createdBy; private String createdAt; private String modifiedBy; @@ -147,6 +147,10 @@ public String getModifiedAt() { return modifiedAt; } + public String getAutoApproveError() { + return autoApproveError; + } + public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } @@ -163,6 +167,10 @@ public void setModifiedAt(String modifiedAt) { this.modifiedAt = modifiedAt; } + public void setAutoApproveError(String error) { + this.autoApproveError = error; + } + /** * Return bean name as PerunBean does. * @@ -181,6 +189,7 @@ public String toString() { ", fedInfo='" + getFedInfo() + '\'' + ", type='" + getType().toString() + '\'' + ", state='" + getState().toString() + '\'' + + ", autoApproveError='" + getAutoApproveError() + '\'' + ", extSourceName='" + getExtSourceName() + '\'' + ", extSourceType='" + getExtSourceType() + '\'' + ", extSourceLoa='" + getExtSourceLoa() + '\'' + @@ -188,8 +197,7 @@ public String toString() { ", created_at='" + getCreatedAt() + '\'' + ", created_by='" + getCreatedBy() + '\'' + ", modified_at='" + getModifiedAt() + '\'' + - ", modified_by='" + getModifiedBy() + '\'' + - ']'; + ", modified_by='" + getModifiedBy() + '\'' + ']'; } } diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 5537d43095..f584b42cde 100644 --- a/perun-base/src/test/resources/test-schema.sql +++ b/perun-base/src/test/resources/test-schema.sql @@ -517,6 +517,7 @@ create table application ( state varchar, --state of application (new/verified/approved/rejected) extSourceLoa integer, --level of assurance of user by external source group_id integer, --identifier of group (groups.id) if application is for group + auto_approve_error varchar, --error that occurred during automatic approval created_at timestamp default statement_timestamp() not null, created_by varchar default user not null, modified_at timestamp default statement_timestamp() not null, diff --git a/perun-cli/Perun/beans/Application.pm b/perun-cli/Perun/beans/Application.pm index 6e80a8ffe5..1edf8371fe 100644 --- a/perun-cli/Perun/beans/Application.pm +++ b/perun-cli/Perun/beans/Application.pm @@ -66,7 +66,14 @@ sub TO_JSON $type = undef; } - return { id => $id, vo => $vo, group => $group, user => $user, type => $type, state => $state }; + my $autoApproveError; + if (defined($self->{_autoApproveError})) { + $autoApproveError = $self->{_autoApproveError}; + } else { + $autoApproveError = undef; + } + + return { id => $id, vo => $vo, group => $group, user => $user, type => $type, state => $state, autoApproveError => $autoApproveError }; } sub getId @@ -111,6 +118,12 @@ sub getState return $self->{_state}; } +sub getAutoApproveError +{ + my $self = shift; + + return $self->{_autoApproveError}; +} sub getActor { @@ -145,11 +158,12 @@ sub getCommonArrayRepresentation { my $self = shift; return ($self->{_id}, $self->{_type}, $self->{_state}, $self->{_vo}->{id} . " / " . $self->{_vo}->{shortName}, ((defined $self->{_group}) ? $self->{_group}->{id} . " / " . $self->{_group}->{name} : ""), - ((defined $self->{_user}) ? $self->getUserDisplayName : $self->{_createdBy} . " / " . $self->{_extSourceName})); + ((defined $self->{_user}) ? $self->getUserDisplayName : $self->{_createdBy} . " / " . $self->{_extSourceName}), + ((defined $self->{_autoApproveError}) ? $self->{_autoApproveError} : "")); } sub getCommonArrayRepresentationHeading { - return ('ID', 'Type', 'State', 'Vo', 'Group', 'Submitted by'); + return ('ID', 'Type', 'State', 'Vo', 'Group', 'Submitted by', 'Automatic approval error'); } 1; diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 4c5c1c54db..60d8391721 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -9,6 +9,7 @@ 3.2.11 ALTER TABLE vos DROP constraint vo_u; ALTER TABLE vos ADD CONSTRAINT vo_u unique (short_name); +ALTER TABLE application ADD COLUMN auto_approve_error varchar; UPDATE configurations set value='3.2.11' WHERE property='DATABASE VERSION'; 3.2.10 diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index ffee74c992..ecec653d05 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -515,6 +515,7 @@ create table application ( state varchar, --state of application (new/verified/approved/rejected) extSourceLoa integer, --level of assurance of user by external source group_id integer, --identifier of group (groups.id) if application is for group + auto_approve_error varchar, --error that occurred during automatic approval created_at timestamp default statement_timestamp() not null, created_by varchar default user not null, modified_at timestamp default statement_timestamp() not null, diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 70d350870a..69bb35a2e7 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -1078,6 +1078,7 @@ components: createdAt: { type: string } modifiedBy: { type: string } modifiedAt: { type: string } + autoApproveError: { type: string } RichApplication: allOf: diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java index 112f686ae4..e86a982b21 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java @@ -108,6 +108,7 @@ public class MailManagerImpl implements MailManager { private static final String FIELD_ACTOR = "{actor}"; private static final String FIELD_EXT_SOURCE = "{extSource}"; private static final String FIELD_CUSTOM_MESSAGE = "{customMessage}"; + private static final String FIELD_AUTO_APPROVE_ERROR = "{autoApproveError}"; private static final String FIELD_FIRST_NAME = "{firstName}"; private static final String FIELD_LAST_NAME = "{lastName}"; private static final String FIELD_ERRORS = "{errors}"; @@ -1307,6 +1308,7 @@ private String buildInviteURL(Vo vo, Group group, String text) { * {phone} - user phone submitted on application or stored in a system * * {customMessage} - message passed by the admin to mail (e.g. reason of application reject) + * {autoApproveError} - error that caused automatic approval failure * {errors} - include errors, which occurred when processing registrar actions * (e.g. login reservation errors passed to mail for VO admin) * @@ -1362,6 +1364,15 @@ private String substituteCommonStrings(Application app, List additionalAttributes = BeansUtils.stringToMapOfAttributes(app.getFedInfo()); @@ -3808,19 +3807,15 @@ private void tryToAutoApproveApplication(PerunSession sess, Application app) thr membersManager.getMemberByUser(sess, app.getVo(), u); } else { // user not found or null, hence can't be member of VO -> do not approve. + setAutoApproveErrorToApplication(app, "This application is waiting for approval of the VO application, which must be approved by the VO manager first. After that, this application will be automatically approved."); return; } } else { // user known, but maybe not member of a vo membersManager.getMemberByUser(sess, app.getVo(), app.getUser()); } - } catch (MemberNotExistsException ex) { - return; - } catch (UserNotExistsException ex) { - return; - } catch (UserExtSourceNotExistsException ex) { - return; - } catch (ExtSourceNotExistsException ex) { + } catch (MemberNotExistsException | UserNotExistsException | UserExtSourceNotExistsException | ExtSourceNotExistsException ex) { + setAutoApproveErrorToApplication(app, "This application is waiting for approval of the VO application, which must be approved by the VO manager first. After that, this application will be automatically approved."); return; } } @@ -3854,14 +3849,10 @@ private void tryToAutoApproveApplication(PerunSession sess, Application app) thr } } catch (Exception ex) { - - ArrayList list = new ArrayList<>(); - list.add(ex); - getMailManager().sendMessage(app, MailType.APP_ERROR_VO_ADMIN, null, list); - + setAutoApproveErrorToApplication(app, ex.getMessage()); + getMailManager().sendMessage(app, MailType.APP_ERROR_VO_ADMIN, null, List.of(ex)); throw ex; } - } /** @@ -4803,6 +4794,16 @@ private void fixDependencies(ApplicationForm form, Map idsTran } } + /** + * Sets error that occurred during automatic approval of application. + * + * @param application application + * @param error error + */ + private void setAutoApproveErrorToApplication(Application application, String error) { + jdbc.update("UPDATE application SET auto_approve_error=? WHERE id=?", error, application.getId()); + } + /** * Returns number of embedded groups form items. */ @@ -4942,15 +4943,15 @@ private static ResultSetExtractor> getPaginatedApplic } // FIXME - we are retrieving GROUP name using only "short_name" so it's not same as getGroupById() - static final String APP_SELECT = "select a.id as id,a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + - "a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + + static final String APP_SELECT = "select a.id as id, a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + + "a.user_id as user_id, a.auto_approve_error as auto_approve_error, a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + "v.name as vo_name, v.short_name as vo_short_name, v.created_by as vo_created_by, v.created_at as vo_created_at, v.created_by_uid as vo_created_by_uid, v.modified_by as vo_modified_by, " + "v.modified_at as vo_modified_at, v.modified_by_uid as vo_modified_by_uid, g.name as group_name, g.dsc as group_description, g.created_by as group_created_by, g.created_at as group_created_at, g.modified_by as group_modified_by, g.created_by_uid as group_created_by_uid, g.modified_by_uid as group_modified_by_uid," + "g.modified_at as group_modified_at, g.vo_id as group_vo_id, g.parent_group_id as group_parent_group_id, g.uu_id as group_uu_id, u.first_name as user_first_name, u.last_name as user_last_name, u.middle_name as user_middle_name, " + "u.title_before as user_title_before, u.title_after as user_title_after, u.service_acc as user_service_acc, u.sponsored_acc as user_sponsored_acc , u.uu_id as user_uu_id from application a left outer join vos v on a.vo_id = v.id left outer join groups g on a.group_id = g.id left outer join users u on a.user_id = u.id"; - static final String APP_SELECT_PAGE = "select a.id as id,a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + - "a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + + static final String APP_SELECT_PAGE = "select a.id as id, a.vo_id as vo_id, a.group_id as group_id, a.apptype as apptype, a.fed_info as fed_info,a.state as state," + + "a.auto_approve_error as auto_approve_error, a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + "v.name as vo_name, v.short_name as vo_short_name, v.created_by as vo_created_by, v.created_at as vo_created_at, v.created_by_uid as vo_created_by_uid, v.modified_by as vo_modified_by, " + "v.modified_at as vo_modified_at, v.modified_by_uid as vo_modified_by_uid, g.name as group_name, g.dsc as group_description, g.created_by as group_created_by, g.created_at as group_created_at, g.modified_by as group_modified_by, g.created_by_uid as group_created_by_uid, g.modified_by_uid as group_modified_by_uid," + "g.modified_at as group_modified_at, g.vo_id as group_vo_id, g.parent_group_id as group_parent_group_id, g.uu_id as group_uu_id, u.first_name as user_first_name, u.last_name as user_last_name, u.middle_name as user_middle_name, " + @@ -5020,6 +5021,7 @@ private static ResultSetExtractor> getPaginatedApplic app.setCreatedBy(resultSet.getString("app_created_by")); app.setModifiedAt(resultSet.getString("app_modified_at")); app.setModifiedBy(resultSet.getString("app_modified_by")); + app.setAutoApproveError(resultSet.getString("auto_approve_error")); return app; diff --git a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl index 89f7e0491f..8800149fe2 100755 --- a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl +++ b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl @@ -93,11 +93,11 @@ $objectExamples{"List<RichGroup>"} = $listPrepend . $objectExamples{"RichGroup"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichGroup>"}; -$objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; +$objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; $objectExamples{"List<Application>"} = $listPrepend . $objectExamples{"Application"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Application>"}; -$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; +$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; $objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java index 95a0787c1a..4c2e1ae391 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java @@ -179,6 +179,24 @@ public final native void setExtSourceName(String extSourceName) /*-{ this.extSourceName = extSourceName; }-*/; + /** + * Get error that occurred during the automatic approval of the application. + * + * @return error + */ + public final native String getAutoApproveError() /*-{ + return this.autoApproveError; + }-*/; + + /** + * Set error that occurred during the automatic approval of the application. + * + * @param autoApproveError error + */ + public final native void setAutoApproveError(String autoApproveError) /*-{ + this.autoApproveError = autoApproveError; + }-*/; + /** * Get extSourceType * @return extSourceType diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java index d9be0f64c4..3668606c51 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java @@ -324,6 +324,7 @@ private Widget availableTagsTab() { "
{mailFooter} - common mail footer defined by VO" + "
{errors} - errors description, what happened while processing new application. Useful for VO administrators." + "
{customMessage} - optional message passed by administrators when rejecting an application" + + "
{autoApproveError} - error that caused automatic approval failure" + "
{fromApp-itemName} - value of a form item in user's application. You MUST specify the itemName, e.g. {fromApp-mail} will print value of item with short name 'mail' from user's application." + "

User related:" + diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/EditMailTabItem.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/EditMailTabItem.java index 268944ce10..c06e527245 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/EditMailTabItem.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/EditMailTabItem.java @@ -258,6 +258,7 @@ private Widget availableTagsTab() { "
{mailFooter} - common mail footer defined by VO" + "
{errors} - errors description, what happened while processing new application. Useful for VO administrators." + "
{customMessage} - optional message passed by administrators when rejecting an application" + + "
{autoApproveError} - error that caused automatic approval failure" + "
{fromApp-itemName} - value of a form item in user's application. You MUST specify the itemName, e.g. {fromApp-mail} will print value of item with short name 'mail' from user's application." + "

User related:" +