From 8adea845a43887cbca713463aaaf55de4fff1df9 Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Wed, 23 Aug 2023 18:23:36 +0200 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Cascade=20to=20par?= =?UTF-8?q?ent=20gr.=20when=20deciding=20gr.=20TO=20recipients?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sending out notification to managers or configured TO recipients for groups, try to lookup recipients in group hierarchy instead of using the VO level directly. This means that if the group does not have "TO" set and there is noone to send notification to in corresponding role, the lookup will be tried one level higher in the group hierarchy, cascading to the top level group and only then to VO level. DEPLOYMENT NOTE: Changed behaviour might cause sending notifications to managers or configured TO recipients in parent group rather than to VO. --- .../perun/registrar/impl/MailManagerImpl.java | 238 ++++++++++++------ 1 file changed, 155 insertions(+), 83 deletions(-) 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 9f8e788dbc..c19d9a8764 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 @@ -1,10 +1,21 @@ package cz.metacentrum.perun.registrar.impl; -import java.io.*; +import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,8 +43,31 @@ import cz.metacentrum.perun.audit.events.MailManagerEvents.MailForVoIdUpdated; import cz.metacentrum.perun.audit.events.MailManagerEvents.MailSending; import cz.metacentrum.perun.audit.events.MailManagerEvents.MailSentForApplication; -import cz.metacentrum.perun.core.api.*; -import cz.metacentrum.perun.core.api.exceptions.*; +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.AuthzResolver; +import cz.metacentrum.perun.core.api.BeansUtils; +import cz.metacentrum.perun.core.api.ExtSourcesManager; +import cz.metacentrum.perun.core.api.Group; +import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunBean; +import cz.metacentrum.perun.core.api.PerunClient; +import cz.metacentrum.perun.core.api.PerunPrincipal; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.RichUser; +import cz.metacentrum.perun.core.api.Role; +import cz.metacentrum.perun.core.api.RoleManagementRules; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.Vo; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ConsistencyErrorException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; +import cz.metacentrum.perun.core.api.exceptions.InvalidHtmlInputException; +import cz.metacentrum.perun.core.api.exceptions.PerunException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; +import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; import cz.metacentrum.perun.core.bl.AttributesManagerBl; import cz.metacentrum.perun.core.bl.GroupsManagerBl; import cz.metacentrum.perun.core.bl.MembersManagerBl; @@ -56,6 +90,7 @@ import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import cz.metacentrum.perun.core.bl.PerunBl; import org.springframework.jdbc.core.JdbcPerunTemplate; @@ -71,10 +106,20 @@ import cz.metacentrum.perun.registrar.MailManager; import cz.metacentrum.perun.registrar.RegistrarManager; -import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.*; import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_FROM_EMAIL; import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_FROM_NAME_EMAIL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_HTML_MAIL_FOOTER; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_LANGUAGE_EMAIL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_MAIL_FOOTER; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_REGISTRAR_URL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_GROUP_TO_EMAIL; import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_FROM_EMAIL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_FROM_NAME_EMAIL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_HTML_MAIL_FOOTER; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_LANGUAGE_EMAIL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_MAIL_FOOTER; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_REGISTRAR_URL; +import static cz.metacentrum.perun.registrar.impl.RegistrarManagerImpl.URN_VO_TO_EMAIL; public class MailManagerImpl implements MailManager { @@ -524,22 +569,16 @@ public void sendMessage(Application app, MailType mailType, String reason, List< // different behavior based on mail type switch (mail.getMailType()) { case APP_CREATED_USER: - sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_CREATED_USER); - break; case APPROVABLE_GROUP_APP_USER: - sendUserMessage(app, mail, data, reason, exceptions, MailType.APPROVABLE_GROUP_APP_USER); - break; - case APP_CREATED_VO_ADMIN: - appCreatedVoAdmin(app, mail, data, reason, exceptions); + case APP_APPROVED_USER: + case APP_REJECTED_USER: + sendUserMessage(app, mail, data, reason, exceptions, mail.getMailType()); break; case MAIL_VALIDATION: mailValidation(app, mail, data, reason, exceptions); break; - case APP_APPROVED_USER: - sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_APPROVED_USER); - break; - case APP_REJECTED_USER: - sendUserMessage(app, mail, data, reason, exceptions, MailType.APP_REJECTED_USER); + case APP_CREATED_VO_ADMIN: + appCreatedVoAdmin(app, mail, data, reason, exceptions); break; case APP_ERROR_VO_ADMIN: appErrorVoAdmin(app, mail, data, reason, exceptions); @@ -1091,69 +1130,57 @@ private void setFromAndReplyTo(MimeMessage message, String fromName, String from /** * Get proper values "TO" for mail message based on VO or GROUP attribute "toEmail". *

- * If group attribute not set and is group application, notify all corresponding group admins (admin roles configured to receive GROUP notifications). - * In case no such admins not set, use VO attribute. - * If Vo attribute not set (both group or vo application), notify all corresponding vo admins (admin roles configured to receive VO notifications). - * Otherwise, BACKUP_FROM address will be used. + * If group attribute is not set and Application is a group application, notify all corresponding group admins + * (admin roles configured to receive GROUP notifications). + * In case no such admins are set, try the same procedure on parent group (if exists) + * When reaches the top-level group and no recipients have been identified, repeat the same procedure on VO level (without cascading). + * When no recipient is identified on the VO level or some error happens, BACKUP_TO address will be used. * * @param app application to decide if it's VO or Group application - * @return list of mail addresses to send mail to + * @return Set of mail addresses to send mail to */ - private List getToMailAddresses(Application app) { - List result = new ArrayList<>(); - - // get proper value from attribute + private Set getToMailAddresses(Application app) { + Set result = new HashSet<>(); try { - Attribute attrSenderEmail; - - if (app.getGroup() == null) { - // it is a VO application - return getMailsFromVo(app, URN_VO_TO_EMAIL); - } - - attrSenderEmail = attrManager.getAttribute(registrarSession, app.getGroup(), URN_GROUP_TO_EMAIL); - - if (attrSenderEmail == null || attrSenderEmail.getValue() == null) { - // not specified toEmail group attribute - - // try to use all group admins with specified preferred emails - List rolesToNotify = getNotificationReceiverRoles(GROUP); - List admins = new ArrayList<>(); - for (String role : rolesToNotify) { - admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, app.getGroup(), role)); - } - result.addAll(getUserPreferredMails(registrarSession, admins)); - result = result.stream().distinct().toList(); - + // Check group and its parent group hierarchy first + Group currentGroup = app.getGroup(); + while (currentGroup != null) { + result = getToEmailsGroupLevel(currentGroup); if (!result.isEmpty()) { - return result; + break; } - return getMailsFromVo(app, URN_VO_TO_EMAIL); + currentGroup = getParentGroupForMailAddresses(currentGroup); } - - // specified toEmail group attribute, use valid emails - ArrayList value = attrSenderEmail.valueAsList(); - for (String adr : value) { - if (adr != null && !adr.isEmpty()) { - result.add(adr); - } + // VO application or fallback to VO level + if (app.getGroup() == null || result.isEmpty()) { + result = getToEmailsVoLevel(app.getVo()); } - } catch (Exception ex) { // we don't care about exceptions here - we have backup TO/FROM address - if (app.getGroup() == null) { - log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}", URN_VO_TO_EMAIL, ex); - } else { - log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}", URN_GROUP_TO_EMAIL, ex); - } + log.error("[MAIL MANAGER] Exception thrown when getting TO email from an attribute {}. Ex: {}", + app.getGroup() == null ? URN_VO_TO_EMAIL : URN_GROUP_TO_EMAIL, ex); // set backup result.clear(); + } + // no recipients found for Group and Vo, use backup + if (result.isEmpty()) { result.add(getPropertyFromConfiguration("backupTo")); } - return result; } + private Group getParentGroupForMailAddresses(Group group) { + if (group != null && group.getParentGroupId() != null) { + try { + return groupsManager.getGroupById(registrarSession, group.getParentGroupId()); + } catch (GroupNotExistsException e) { + log.error("[MAIL MANAGER] Inconsistency detected - parent (in hierarchy) group with ID '{}' of group '{}' does not exist", + group.getParentGroupId(), group); + } + } + return null; + } + /** * Returns list of roles, which should be notified for object's notification type * @return list of role names @@ -1987,7 +2014,7 @@ private void appCreatedVoAdmin(Application app, ApplicationMail mail, List toEmail = getToMailAddresses(app); + Set toEmail = getToMailAddresses(app); for (String email : toEmail) { setRecipient(message, email); try { @@ -2144,7 +2171,7 @@ private void appErrorVoAdmin(Application app, ApplicationMail mail, List toEmail = getToMailAddresses(app); + Set toEmail = getToMailAddresses(app); for (String email : toEmail) { setRecipient(message, email); @@ -2529,45 +2556,90 @@ private void setHtmlMessageWithAltPlainTextMessage (MimeMessage message, String } /** - * Get notification emails for given VO. If toEmail is not specified, + * Get notification emails for given VO. If toEmail (URN_VO_TO_EMAIL) has no value set, * all configured roles for receiving VO notifications are used. * - * @param app Application to get VO from - * @param voEmailAttr String name of given email attribute - * @return list of emails used for sending notifications. + * @param vo Virtual Organization for which the lookup is performed. If null, empty result will be returned. + * @return List of emails used for sending notifications (can be empty). */ - private List getMailsFromVo(Application app, String voEmailAttr) - throws AttributeNotExistsException, WrongAttributeAssignmentException { - - List emails = new ArrayList<>(); + private Set getToEmailsVoLevel(Vo vo) + throws AttributeNotExistsException, WrongAttributeAssignmentException + { + if (vo == null) { + return new HashSet<>(); + } + Attribute toEmailAttr = attrManager.getAttribute(registrarSession, vo, URN_VO_TO_EMAIL); + Set emails = getToEmailsFromAttribute(toEmailAttr); + if (emails.isEmpty()) { + emails = getToEmailsViaRoles(VO, vo); + } + return emails; + } - Attribute attrToEmail = attrManager.getAttribute(registrarSession, app.getVo(), voEmailAttr); + /** + * Get notification emails for given Group. If toEmail (URN_GROUP_TO_EMAIL) has no value set, + * all configured roles for receiving GROUP notifications are used. + * + * @param group Group for which the lookup is performed. If null, empty result will be returned. + * @return List of emails used for sending notifications (can be empty). + */ + private Set getToEmailsGroupLevel(Group group) + throws AttributeNotExistsException, WrongAttributeAssignmentException + { + if (group == null) { + return new HashSet<>(); + } + Attribute toEmailAttr = attrManager.getAttribute(registrarSession, group, URN_GROUP_TO_EMAIL); + Set emails = getToEmailsFromAttribute(toEmailAttr); + if (emails.isEmpty()) { + emails = getToEmailsViaRoles(GROUP, group); + } + return emails; + } + /** + * Get "TO" email addresses of notification recipient(s) from the attribute + * @param toEmailAttr Attribute with the recipients as value + * @return Set of identified emails. Can be empty if attr has no non-blank item in array value or no value at all. + */ + private Set getToEmailsFromAttribute(Attribute toEmailAttr) { // if there are any pre-defined VO emailTo attributes, use them - if (attrToEmail != null && attrToEmail.getValue() != null) { - ArrayList value = attrToEmail.valueAsList(); + Set emails = new HashSet<>(); + if (toEmailAttr != null && toEmailAttr.getValue() != null) { + ArrayList value = toEmailAttr.valueAsList(); for (String adr : value) { - if (adr != null && !adr.isEmpty()) { + if (StringUtils.hasText(adr)) { emails.add(adr); } } - return emails; } + return emails; + } - // in case no pre-defined attribute was found, use all configured roles with specified preferred email + /** + * Get "TO" emails from notification receiver roles of the specified object. + * @param object String specification of the object + * @param objectInstance instance of the object + * @return Set of identified emails. Can be empty if not roles or users in the role have been identified, + * or they do not have email address set. + * @throws WrongAttributeAssignmentException + * @throws AttributeNotExistsException + */ + private Set getToEmailsViaRoles(String object, PerunBean objectInstance) + throws WrongAttributeAssignmentException, AttributeNotExistsException + { + Set emails = new HashSet<>(); try { - List rolesToNotify = getNotificationReceiverRoles(VO); + List rolesToNotify = getNotificationReceiverRoles(object); List admins = new ArrayList<>(); for (String role : rolesToNotify) { - admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, app.getVo(), role)); + admins.addAll(AuthzResolverBlImpl.getRichAdmins(registrarSession, objectInstance, role)); } emails.addAll(getUserPreferredMails(registrarSession, admins)); - emails = emails.stream().distinct().toList(); + return emails; } catch (RoleCannotBeManagedException e) { throw new InternalErrorException(e); } - - return emails; } /** From f3bee326dd92665acf5b2af4ec85a1280549bf54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:46:00 +0000 Subject: [PATCH 02/17] fix(deps): update dependency com.google.apis:google-api-services-admin-directory to directory_v1-rev20230822-2.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 281bf3b645..9c9ae8314d 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 1.6 0.5.10 9.1.22 - directory_v1-rev20230814-2.0.0 + directory_v1-rev20230822-2.0.0 2.2.21.Final 3.2.10.Final 1.1.0.GA From 1c536925f9bd68f93e8aeb1e476e3e76a1964895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Mon, 11 Sep 2023 06:57:42 +0200 Subject: [PATCH 03/17] feat(cli): added getRichMember method to the perl client API --- perun-cli/Perun/MembersAgent.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/perun-cli/Perun/MembersAgent.pm b/perun-cli/Perun/MembersAgent.pm index 758c65ba44..f37df334d4 100644 --- a/perun-cli/Perun/MembersAgent.pm +++ b/perun-cli/Perun/MembersAgent.pm @@ -58,6 +58,11 @@ sub getRichMembers return Perun::Common::callManagerMethod('getRichMembers', '[]RichMember', @_); } +sub getRichMember +{ + return Perun::Common::callManagerMethod('getRichMember', 'RichMember', @_); +} + sub getRichMembersWithAttributes { return Perun::Common::callManagerMethod('getRichMembersWithAttributes', '[]RichMember', @_); From 4af8990208b0fd85292617828753c8805e977636 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 19:23:05 +0000 Subject: [PATCH 04/17] chore(deps): update dependency @semantic-release/github to v9.0.5 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 928bc63c37..d7b81a7283 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.4", + "@semantic-release/github": "9.0.5", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", @@ -873,9 +873,9 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.4.tgz", - "integrity": "sha512-kQCGFAsBErvCR6hzNuzu63cj4erQN2krm9zQlg8vl4j5X0mL0d/Ras0wmL5Gkr1TuSS2lweME7M4J5zvtDDDSA==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.5.tgz", + "integrity": "sha512-d1ZZjMvXpSa4E1L3XjdNOqgUy00o9QZX55L75pMsb/w+1NV6CCfDYOvH8qwKygHS/rKzI3FkBTcR40ahOodsgg==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", diff --git a/package.json b/package.json index dfa660a96d..6e44f2f0de 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.4", + "@semantic-release/github": "9.0.5", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", From cf542ed0c16a652b14e58b5244ef90689d261c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Wed, 13 Sep 2023 07:12:56 +0200 Subject: [PATCH 05/17] feat(core): sort users by IDs when synchronizing LDAP - When synchronizing whole LDAP sort processed users by their IDs to get more consistent log output. --- .../cz/metacentrum/perun/ldapc/beans/UserSynchronizer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/beans/UserSynchronizer.java b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/beans/UserSynchronizer.java index 7abb4e888f..c606954103 100644 --- a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/beans/UserSynchronizer.java +++ b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/beans/UserSynchronizer.java @@ -14,7 +14,6 @@ import cz.metacentrum.perun.ldapc.model.PerunUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -22,6 +21,7 @@ import javax.naming.Name; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -115,6 +115,7 @@ public void synchronizeUsers() { log.debug("Getting list of users"); List users = perun.getUsersManagerBl().getUsers(ldapcManager.getPerunSession()); + users.sort(Comparator.comparingInt(User::getId)); Set presentUsers = new HashSet(users.size()); syncExecutor.setCorePoolSize(5); From 0f0ea390338dd58e23e2dbc34e5853ef5f9243b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Wed, 13 Sep 2023 12:54:58 +0200 Subject: [PATCH 06/17] fix: fixed definition of logback in perun-auditlogger Removed problematic property --- perun-auditlogger/src/main/resources/logback.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/perun-auditlogger/src/main/resources/logback.xml b/perun-auditlogger/src/main/resources/logback.xml index fa2250646a..aa058fc2a4 100644 --- a/perun-auditlogger/src/main/resources/logback.xml +++ b/perun-auditlogger/src/main/resources/logback.xml @@ -35,7 +35,6 @@ perun_audit true - false %msg From f46ba67d3c3504c6f2c07b34c46aedf3a76e21fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Thu, 14 Sep 2023 14:40:23 +0200 Subject: [PATCH 07/17] fix: minimize default logging for perun-auditlogger - Switch default logging to "info". --- .../auditlogger/service/impl/AuditLoggerManagerImpl.java | 4 ++-- perun-auditlogger/src/main/resources/logback.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/perun-auditlogger/src/main/java/cz/metacentrum/perun/auditlogger/service/impl/AuditLoggerManagerImpl.java b/perun-auditlogger/src/main/java/cz/metacentrum/perun/auditlogger/service/impl/AuditLoggerManagerImpl.java index f43e80f2ed..8d7ee1fdbd 100644 --- a/perun-auditlogger/src/main/java/cz/metacentrum/perun/auditlogger/service/impl/AuditLoggerManagerImpl.java +++ b/perun-auditlogger/src/main/java/cz/metacentrum/perun/auditlogger/service/impl/AuditLoggerManagerImpl.java @@ -36,13 +36,13 @@ public void startProcessingEvents() { eventProcessorThread = new Thread(eventLogger); eventProcessorThread.start(); - log.debug("Event processor thread started."); + log.info("Event processor thread started."); System.out.println("Event processor thread started."); } public void stopProcessingEvents() { eventProcessorThread.interrupt(); - log.debug("Event processor thread interrupted."); + log.info("Event processor thread interrupted."); System.out.println("Event processor thread interrupted."); } diff --git a/perun-auditlogger/src/main/resources/logback.xml b/perun-auditlogger/src/main/resources/logback.xml index aa058fc2a4..9945236e1f 100644 --- a/perun-auditlogger/src/main/resources/logback.xml +++ b/perun-auditlogger/src/main/resources/logback.xml @@ -26,7 +26,7 @@ - + From bfe49c48982f711bef80294dc0456dda82e9b189 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Sep 2023 13:08:17 +0000 Subject: [PATCH 08/17] chore(deps): update dependency @semantic-release/github to v9.0.6 --- package-lock.json | 28 ++++++++++++++-------------- package.json | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7b81a7283..d98302bd0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.5", + "@semantic-release/github": "9.0.6", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", @@ -873,9 +873,9 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.5.tgz", - "integrity": "sha512-d1ZZjMvXpSa4E1L3XjdNOqgUy00o9QZX55L75pMsb/w+1NV6CCfDYOvH8qwKygHS/rKzI3FkBTcR40ahOodsgg==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.6.tgz", + "integrity": "sha512-GBGt9c3c2UdSvso4jcyQQSUpZA9hbfHqGQerZKN9WvVzCIkaBy8xkhOyiFVX08LjRHHT/H221SJNBLtuihX5iw==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", @@ -883,7 +883,7 @@ "@octokit/plugin-retry": "^6.0.0", "@octokit/plugin-throttling": "^7.0.0", "@semantic-release/error": "^4.0.0", - "aggregate-error": "^4.0.1", + "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "globby": "^13.1.4", @@ -912,31 +912,31 @@ } }, "node_modules/@semantic-release/github/node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "dependencies": { - "clean-stack": "^4.0.0", + "clean-stack": "^5.2.0", "indent-string": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/github/node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "dependencies": { "escape-string-regexp": "5.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 6e44f2f0de..a582d3c016 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.5", + "@semantic-release/github": "9.0.6", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", From ba1bb156580afffc4714343c6d4c834113762219 Mon Sep 17 00:00:00 2001 From: Rastislav Krutak <492918@mail.muni.cz> Date: Mon, 18 Sep 2023 10:57:17 +0200 Subject: [PATCH 09/17] feat(core): allow resource managers to read subgroup managers - Allowed RESOURCEADMIN, TRUSTEDFACILITYADMIN and RESOURCEOBSERVER to read GROUPADMIN and GROUPMEMBERSHIPMANAGER in the VO. - This way the resource managers know who to contact in the matter of membership in the assigned groups. --- perun-base/src/main/resources/perun-roles.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 14b33cead9..80089e4376 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -8310,6 +8310,9 @@ perun_roles_management: - GROUPOBSERVER: Group - GROUPMEMBERSHIPMANAGER: Group - SPREGAPPLICATION: + - RESOURCEADMIN: Vo + - RESOURCEOBSERVER: Vo + - TRUSTEDFACILITYADMIN: Vo associated_read_roles: - GROUPOBSERVER assignable_to_attributes: true @@ -8371,6 +8374,9 @@ perun_roles_management: - GROUPADMIN: Group - GROUPOBSERVER: Group - GROUPMEMBERSHIPMANAGER: Group + - RESOURCEADMIN: Vo + - RESOURCEOBSERVER: Vo + - TRUSTEDFACILITYADMIN: Vo associated_read_roles: [] assignable_to_attributes: true display_name: "Group membership manager" From f20ec3dd4664c89eb563e1003d9ae023afd5367d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:42:58 +0000 Subject: [PATCH 10/17] fix(deps): update dependency org.xhtmlrenderer:flying-saucer-pdf to v9.2.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81cd84254c..95eefc9e8c 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 2.1.4 1.6 0.5.10 - 9.1.22 + 9.2.2 directory_v1-rev20230822-2.0.0 2.2.21.Final 3.2.10.Final From 26de9abf70368b5fd2109cdd4ac47f1b283e1b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Tue, 19 Sep 2023 13:50:38 +0200 Subject: [PATCH 11/17] fix(core): properly resolve members removal from authoritative groups - Properly resolve members removal from the last authoritative group during the group synchronization. - We first remove the member from the group, as this should always happen, then when all transitive group memberships are resolved we ask if there are any remaining authoritative groups and call the necessary logic (disabling/removing member). - This enables use-case when authoritative groups are in hierarchy (group structure synchronization). - Updated javadoc. --- .../core/blImpl/GroupsManagerBlImpl.java | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 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 f27278ed31..9e99917297 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 @@ -4133,8 +4133,9 @@ private boolean isAuthoritative(PerunSession sess, Group group) throws WrongAttr } /** - * Remove member from authoritative group. - * If this is the last authoritative group of this member, delete him from vo, otherwise just disable him in vo. + * Removes member from group which supposed to be authoritative. + * Changes member's status to DISABLED if he was removed from his last authoritative group within the VO. + * If member's previous status was INVALID delete him from VO instead of disabling. * * @param sess perun session * @param group authoritative group @@ -4144,39 +4145,30 @@ private boolean isAuthoritative(PerunSession sess, Group group) throws WrongAttr * @throws MemberAlreadyRemovedException if member was already removed */ private void removeMemberFromAuthoritativeGroup(PerunSession sess, Group group, RichMember memberToRemove) throws GroupNotExistsException, NotGroupMemberException, MemberAlreadyRemovedException { - List memberAuthoritativeGroups = null; - try { - memberAuthoritativeGroups = getAllAuthoritativeGroupsOfMember(sess, memberToRemove); - } catch (AttributeNotExistsException ex) { - //This means that no authoritative group can exists without this attribute - log.error("Attribute {} doesn't exists.", A_G_D_AUTHORITATIVE_GROUP); - } - //If list of member authoritativeGroups is not null, attribute exists - if (memberAuthoritativeGroups != null) { - memberAuthoritativeGroups.remove(group); + // Always remove member from the group + getPerunBl().getGroupsManagerBl().removeMember(sess, group, memberToRemove); + log.info("Group synchronization {}: Member id {} removed.", group, memberToRemove.getId()); + + // Resolve remaining authoritative group memberships + try { + List memberAuthoritativeGroups = getAllAuthoritativeGroupsOfMember(sess, memberToRemove); if (memberAuthoritativeGroups.isEmpty()) { - //First try to disable member, if is invalid, delete him from Vo + // Member is not in any other authoritative group -> disable him try { getPerunBl().getMembersManagerBl().disableMember(sess, memberToRemove); - log.debug("Group synchronization {}: Member id {} disabled because synchronizer wants to remove him from last authoritativeGroup in Vo.", group, memberToRemove.getId()); - getPerunBl().getGroupsManagerBl().removeMember(sess, group, memberToRemove); - log.info("Group synchronization {}: Member id {} removed.", group, memberToRemove.getId()); + log.debug("Group synchronization {}: Member id {} disabled because synchronizer removed him from last authoritative group in Vo.", group, memberToRemove.getId()); } catch (MemberNotValidYetException ex) { - //Member is still invalid in perun. We can delete him. + // Member has INVALID status in VO. We can delete him from VO. getPerunBl().getMembersManagerBl().deleteMember(sess, memberToRemove); log.info("Group synchronization {}: Member id {} would have been disabled but he has been deleted instead because he was invalid and synchronizer wants to remove him from last authoritativeGroup in Vo.", group, memberToRemove.getId()); } - } else { - //If there is still some other authoritative group for this member, only remove him from group - getPerunBl().getGroupsManagerBl().removeMember(sess, group, memberToRemove); - log.info("Group synchronization {}: Member id {} removed.", group, memberToRemove.getId()); } - //If list of member authoritativeGroups is null, attribute not exists, only remove member from Group - } else { - getPerunBl().getGroupsManagerBl().removeMember(sess, group, memberToRemove); - log.info("Group synchronization {}: Member id {} removed.", group, memberToRemove.getId()); + } catch (AttributeNotExistsException ex) { + // This means that no authoritative group can exist without this attribute + log.error("Attribute {} doesn't exists.", A_G_D_AUTHORITATIVE_GROUP); } + } /** From 16befb05a6c2da8bcedd04541760dd0b9b8ab8d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:42:53 +0000 Subject: [PATCH 12/17] chore(deps): update dependency semantic-release to v21.1.2 --- package-lock.json | 34 +++++++++++++++++----------------- package.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index d98302bd0c..a89cbd3008 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", - "semantic-release": "21.1.1" + "semantic-release": "21.1.2" } }, "node_modules/@babel/code-frame": { @@ -8269,9 +8269,9 @@ "dev": true }, "node_modules/semantic-release": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-21.1.1.tgz", - "integrity": "sha512-OCIazQnaCHdq1F6zfmKS0P7jZakYq0weiqW2mxUWo4H2CDnxelUoa/0Bs/dQatoHc6JFh6lG2HWpusdl93bFcw==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-21.1.2.tgz", + "integrity": "sha512-kz76azHrT8+VEkQjoCBHE06JNQgTgsC4bT8XfCzb7DHcsk9vG3fqeMVik8h5rcWCYi2Fd+M3bwA7BG8Z8cRwtA==", "dev": true, "dependencies": { "@semantic-release/commit-analyzer": "^10.0.0", @@ -8279,7 +8279,7 @@ "@semantic-release/github": "^9.0.0", "@semantic-release/npm": "^10.0.2", "@semantic-release/release-notes-generator": "^11.0.0", - "aggregate-error": "^4.0.1", + "aggregate-error": "^5.0.0", "cosmiconfig": "^8.0.0", "debug": "^4.0.0", "env-ci": "^9.0.0", @@ -8320,31 +8320,31 @@ } }, "node_modules/semantic-release/node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "dependencies": { - "clean-stack": "^4.0.0", + "clean-stack": "^5.2.0", "indent-string": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "dependencies": { "escape-string-regexp": "5.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8430,9 +8430,9 @@ } }, "node_modules/semantic-release/node_modules/hosted-git-info": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", - "integrity": "sha512-ICclEpTLhHj+zCuSb2/usoNXSVkxUSIopre+b1w8NDY9Dntp9LO4vLdHYI336TH8sAqwrRgnSfdkBG2/YpisHA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { "lru-cache": "^10.0.1" diff --git a/package.json b/package.json index a582d3c016..7647a72ed6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", - "semantic-release": "21.1.1" + "semantic-release": "21.1.2" }, "config": { "commitizen": { From 9bc9d1419b5753950531d8b2f8940b27dd561c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Tue, 19 Sep 2023 14:09:52 +0200 Subject: [PATCH 13/17] feat(core): support authoritative groups in group structure synchronization - Set group:def:authoritativeGroup attribute to all subgroups in group structure synchronization based on the value from the base group. --- .../main/java/cz/metacentrum/perun/core/api/GroupsManager.java | 2 ++ .../cz/metacentrum/perun/core/blImpl/GroupsManagerBlImpl.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/GroupsManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/GroupsManager.java index d878207290..14a73a2a7e 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/GroupsManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/GroupsManager.java @@ -78,6 +78,8 @@ public interface GroupsManager { String GROUP_STRUCTURE_SYNCHRO_INTERVAL_ATTRNAME = AttributesManager.NS_GROUP_ATTR_DEF + ":groupStructureSynchronizationInterval"; // Defines if we want to skip updating already existing members in group from extSource (updating attributes etc.) String GROUPLIGHTWEIGHTSYNCHRONIZATION_ATTRNAME = AttributesManager.NS_GROUP_ATTR_DEF + ":lightweightSynchronization"; + // Defines if members should be disabled in VO if they are removed from the last authoritative group + String GROUPAUTHORITATIVEGROUP_ATTRNAME = AttributesManager.NS_GROUP_ATTR_DEF + ":authoritativeGroup"; // Defines if we want to synchronize group structure without group hierarchy String GROUP_FLAT_SYNCHRONIZATION_ATTRNAME = AttributesManager.NS_GROUP_ATTR_DEF + ":flatGroupStructureEnabled"; // Defines the times, when the group has to be synchronized. 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 f27278ed31..e1f1b8b96a 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 @@ -4317,6 +4317,7 @@ private void setUpSynchronizationAttributesForAllSubGroups(PerunSession sess, Gr Attribute membersQueryAttribute = new Attribute(getPerunBl().getAttributesManagerBl().getAttributeDefinition(sess, GroupsManager.GROUPMEMBERSQUERY_ATTRNAME)); Attribute baseMemberExtsource = getPerunBl().getAttributesManagerBl().getAttribute(sess, baseGroup, GroupsManager.GROUPMEMBERSEXTSOURCE_ATTRNAME); Attribute lightWeightSynchronization = getPerunBl().getAttributesManagerBl().getAttribute(sess, baseGroup, GroupsManager.GROUPLIGHTWEIGHTSYNCHRONIZATION_ATTRNAME); + Attribute authoritativeGroup = getPerunBl().getAttributesManagerBl().getAttribute(sess, baseGroup, GroupsManager.GROUPAUTHORITATIVEGROUP_ATTRNAME); Attribute synchronizationInterval = getPerunBl().getAttributesManagerBl().getAttribute(sess, baseGroup, GroupsManager.GROUPSYNCHROINTERVAL_ATTRNAME); Attribute extSourceNameAttr = new Attribute(getPerunBl().getAttributesManagerBl().getAttributeDefinition(sess, GroupsManager.GROUPEXTSOURCE_ATTRNAME)); Attribute synchroEnabled = new Attribute(getPerunBl().getAttributesManagerBl().getAttributeDefinition(sess, GroupsManager.GROUPSYNCHROENABLED_ATTRNAME)); @@ -4341,7 +4342,7 @@ private void setUpSynchronizationAttributesForAllSubGroups(PerunSession sess, Gr //replace question mark for login without prefix (strip prefix from it) membersQueryAttribute.setValue(baseMembersQuery.getValue().toString().replace("?", loginAttribute.valueAsString().replaceFirst(loginPrefix, ""))); - getPerunBl().getAttributesManagerBl().setAttributes(sess, group, Arrays.asList(baseMemberExtsource, lightWeightSynchronization, synchronizationInterval, synchroEnabled, membersQueryAttribute, synchronizationTimes, extSourceNameAttr)); + getPerunBl().getAttributesManagerBl().setAttributes(sess, group, Arrays.asList(baseMemberExtsource, lightWeightSynchronization, authoritativeGroup, synchronizationInterval, synchroEnabled, membersQueryAttribute, synchronizationTimes, extSourceNameAttr)); } } From 811b2173cd7b673677c8f49b10a26126b52ed4da Mon Sep 17 00:00:00 2001 From: David Flor <493294@mail.muni.cz> Date: Fri, 15 Sep 2023 11:09:53 +0200 Subject: [PATCH 14/17] feat(core): allow members to read their group expiration * fixes a bug, where users couldn't extend membership in a group through the profile app DEPLOYMENT NOTE: the groupMembershipExpiration attribute needs to have a new READ policy collection created with the SELF - USER policy --- .../metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java | 1 + 1 file changed, 1 insertion(+) 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 6032f87c90..5abb6b1ecc 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 @@ -7494,6 +7494,7 @@ protected void initialize() { policies.add(Triple.of(Role.GROUPMEMBERSHIPMANAGER, WRITE, RoleObject.Group)); policies.add(Triple.of(Role.VOADMIN, READ, RoleObject.Vo)); policies.add(Triple.of(Role.VOADMIN, WRITE, RoleObject.Vo)); + policies.add(Triple.of(Role.SELF, READ, RoleObject.User)); attributes.put(attr, createInitialPolicyCollections(policies)); //urn:perun:vo:attribute-def:def:blockManualMemberAdding From b807877de45c6bdaf6437a6791a1de72ab183909 Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Mon, 28 Aug 2023 14:52:35 +0200 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Allow=20multiple?= =?UTF-8?q?=20reg.=20modules=20to=20be=20configured?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of specifying single registrar module, allow to specify multiple. When storign in database, specified names are concatenated using `,` as separator. In from handling (any), instead of operating with single module, loop over all specified modules and try to process appropriate action (e.g. call `beforeApprove(...)`). Also updates Perl libs. BREAKING CHANGE: 🧨 ApplicationForm bean property `moduleClassName` replaced with `moduleClassNames`. Type has changed from String to List. Includes database version update and column `module_name` of `application_form` table being renamed to `module_names`. DEPLOYMENT NOTE: requires database update. UI version have to work with updated model of ApplicationForm (`moduleClassName` replaced with field `moduleClassNames`). --- .../registrar/model/ApplicationForm.java | 33 ++++- perun-base/src/test/resources/test-schema.sql | 6 +- perun-cli/Perun/beans/ApplicationForm.pm | 18 +-- perun-cli/updateForm | 22 +-- .../perun/core/impl/GroupsManagerImpl.java | 7 +- .../src/main/resources/postgresChangelog.txt | 4 + perun-db/postgres.sql | 6 +- perun-openapi/openapi.yml | 5 +- .../registrar/impl/RegistrarManagerImpl.java | 130 ++++++++++-------- .../modules/EscPithiaOrganizations.java | 3 +- .../rpc/methods/RegistrarManagerMethod.java | 2 +- .../registrarManager/GetApplicationForm.java | 18 +-- .../perun/webgui/model/ApplicationForm.java | 10 +- 13 files changed, 158 insertions(+), 106 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/ApplicationForm.java b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/ApplicationForm.java index 3b6d8861c4..4429766fc2 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/ApplicationForm.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/ApplicationForm.java @@ -2,6 +2,10 @@ import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.Vo; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; /** * Application form of a VO. Use {@link cz.metacentrum.perun.registrar.RegistrarManager#getFormItems} for items. @@ -16,7 +20,7 @@ public class ApplicationForm { private boolean automaticApproval; private boolean automaticApprovalExtension; private boolean automaticApprovalEmbedded; - private String moduleClassName; + private final List moduleClassNames = new ArrayList<>(); public ApplicationForm() { } @@ -69,12 +73,29 @@ public void setAutomaticApprovalEmbedded(boolean automaticApprovalEmbedded) { this.automaticApprovalEmbedded = automaticApprovalEmbedded; } - public String getModuleClassName() { - return moduleClassName; + public List getModuleClassNames() { + return new ArrayList<>(moduleClassNames); + } + + public void setModuleClassNames(List moduleClassNames) { + this.moduleClassNames.clear(); + for (String moduleClassName : moduleClassNames) { + if (StringUtils.hasText(moduleClassName)) { + this.moduleClassNames.add(moduleClassName); + } + } + } + + public void addModuleClassName(String moduleClassName) { + if (StringUtils.hasText(moduleClassName)) { + this.moduleClassNames.add(moduleClassName); + } } - public void setModuleClassName(String moduleClassName) { - this.moduleClassName = moduleClassName; + public void removeModuleClassName(String moduleClassName) { + if (StringUtils.hasText(moduleClassName)) { + this.moduleClassNames.remove(moduleClassName); + } } /** @@ -94,7 +115,7 @@ public String toString() { ", group='" + getGroup() + '\'' + ", automaticApproval='" + isAutomaticApproval() + '\'' + ", automaticApprovalExtension='" + isAutomaticApprovalExtension() + '\'' + - ", moduleClassName='" + getModuleClassName() + '\'' + + ", moduleClassNames='" + getModuleClassNames() + '\'' + "]"; } diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 9dbaab915e..dda8947bd3 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.17 (don't forget to update insert statement at the end of file) +-- database version 3.2.18 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -539,7 +539,7 @@ create table application_form ( automatic_approval boolean default false not null, --approval of application is automatic automatic_approval_extension boolean default false not null, --approval of extension is automatic automatic_approval_embedded boolean default false not null, --approval of embedded application is automatic - module_name varchar, --name of module which processes application + module_names varchar, --name of modules (separated by comma) which are called when processing application group_id integer, --identifier of group (groups.id) if application is for group created_by_uid integer, modified_by_uid integer, @@ -1908,7 +1908,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.17'); +insert into configurations values ('DATABASE VERSION','3.2.18'); -- 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-cli/Perun/beans/ApplicationForm.pm b/perun-cli/Perun/beans/ApplicationForm.pm index 3629fe58bf..0250ed2e6d 100644 --- a/perun-cli/Perun/beans/ApplicationForm.pm +++ b/perun-cli/Perun/beans/ApplicationForm.pm @@ -59,14 +59,14 @@ sub TO_JSON $automaticApprovalExtension = undef; } - my $moduleClassName; - if (defined($self->{_moduleClassName})) { - $moduleClassName = "$self->{_moduleClassName}"; + my @moduleClassNames; + if (defined($self->{_moduleClassNames})) { + @moduleClassNames = @{$self->{_moduleClassNames}}; } else { - $moduleClassName = undef; + @moduleClassNames = undef; } - return { id => $id, vo => $vo, group => $group, automaticApproval => $automaticApproval, automaticApprovalExtension => $automaticApprovalExtension, moduleClassName => $moduleClassName }; + return { id => $id, vo => $vo, group => $group, automaticApproval => $automaticApproval, automaticApprovalExtension => $automaticApprovalExtension, moduleClassNames => \@moduleClassNames }; } sub getId @@ -138,17 +138,17 @@ sub setAutomaticApprovalExtension } -sub getModuleClassName +sub getModuleClassNames { my $self = shift; - return $self->{_moduleClassName}; + return $self->{_moduleClassNames}; } -sub setModuleClassName +sub setModuleClassNames { my $self = shift; - $self->{_moduleClassName} = shift; + $self->{_moduleClassNames} = shift; } 1; diff --git a/perun-cli/updateForm b/perun-cli/updateForm index 1d2002ab8f..9dc89b5915 100755 --- a/perun-cli/updateForm +++ b/perun-cli/updateForm @@ -16,7 +16,7 @@ sub help { --groupId | -g group id --autApproval | -a automatic approval (1/0) --autApprovalExt | -e automatic approval extension (1/0) - --moduleName | -n name of module + --moduleNames | -n name of module, can be specified multiple times --batch | -b batch --help | -h prints this help @@ -24,17 +24,17 @@ sub help { } our $batch; -my ($voId, $groupId, $moduleName, $app, $appE); +my ($voId, $groupId, @moduleNames, $app, $appE); GetOptions ("help|h" => sub { print help(); exit 0; }, - "batch|b" => \$batch, - "voId|g=i" => \$voId, - "groupId|g=i" => \$groupId, - "autApproval|a=s" => \$app, - "autApprovalExt|e=s" => \$appE, - "moduleName|n=s" => \$moduleName ) || die help(); + "batch|b" => \$batch, + "voId|g=i" => \$voId, + "groupId|g=i" => \$groupId, + "autApproval|a=s" => \$app, + "autApprovalExt|e=s" => \$appE, + 'moduleNames|n=s@{1,}' => \@moduleNames ) || die help(); # Check options if (not defined $groupId and not defined $voId) { die "ERROR: voId or groupId is required \n";} @@ -56,9 +56,9 @@ if (defined $app) { if (defined $appE) { $applicationForm->setAutomaticApprovalExtension($appE); } -if (defined $moduleName) { - $applicationForm->setModuleClassName($moduleName); -} +if (defined \@moduleNames) { + $applicationForm->setModuleClassNames(\@moduleNames); +} $registrarAgent->updateForm( form => $applicationForm ); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/GroupsManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/GroupsManagerImpl.java index efdc2bdf3d..696fdb6a9d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/GroupsManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/GroupsManagerImpl.java @@ -53,6 +53,7 @@ import java.sql.Array; import java.sql.ResultSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -89,7 +90,7 @@ public class GroupsManagerImpl implements GroupsManagerImplApi { protected final static String assignedGroupMappingSelectQuery = groupMappingSelectQuery + ", groups_resources_state.status as groups_resources_state_status, " + "groups_resources_automatic.auto_assign_subgroups as auto_assign_subgroups, groups_resources_state.failure_cause as groups_resources_state_failure_cause, groups_resources_automatic.source_group_id as groups_resources_automatic_source_group_id"; - private static final String applicationFormMappingSelectQuery = "id,vo_id,group_id,automatic_approval,automatic_approval_extension,automatic_approval_embedded,module_name from application_form"; + private static final String applicationFormMappingSelectQuery = "id,vo_id,group_id,automatic_approval,automatic_approval_extension,automatic_approval_embedded,module_names from application_form"; // http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jdbc.html private final JdbcPerunTemplate jdbc; @@ -1405,7 +1406,9 @@ public ApplicationForm getParentApplicationFormForAutoRegistrationGroup(Group gr form.setAutomaticApproval(resultSet.getBoolean("automatic_approval")); form.setAutomaticApprovalExtension(resultSet.getBoolean("automatic_approval_extension")); form.setAutomaticApprovalEmbedded(resultSet.getBoolean("automatic_approval_embedded")); - form.setModuleClassName(resultSet.getString("module_name")); + if (resultSet.getString("module_names") != null) { + form.setModuleClassNames(Arrays.asList(resultSet.getString("module_names").split(","))); + } Vo vo = new Vo(); vo.setId(resultSet.getInt("vo_id")); form.setVo(vo); diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 02666d4f51..a44c06f7d2 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.18 +ALTER TABLE application_form RENAME COLUMN module_name TO module_names; +UPDATE configurations SET value='3.2.18' WHERE property='DATABASE VERSION'; + 3.2.17 ALTER TABLE attribute_critical_actions ADD COLUMN global boolean default false not null; UPDATE configurations SET value='3.2.17' WHERE property='DATABASE VERSION'; diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index c2923381a6..ccb1d12ee8 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.17 (don't forget to update insert statement at the end of file) +-- database version 3.2.18 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -537,7 +537,7 @@ create table application_form ( automatic_approval boolean default false not null, --approval of application is automatic automatic_approval_extension boolean default false not null, --approval of extension is automatic automatic_approval_embedded boolean default false not null, --approval of embedded application is automatic - module_name varchar, --name of module which processes application + module_names varchar, --name of modules (separated by comma) which are called when processing application group_id integer, --identifier of group (groups.id) if application is for group created_by_uid integer, modified_by_uid integer, @@ -2017,7 +2017,7 @@ grant all on blocked_logins to perun; grant all on auto_registration_groups to perun; -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.17'); +insert into configurations values ('DATABASE VERSION','3.2.18'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index c27664956b..08648e8e13 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -1021,7 +1021,10 @@ components: automaticApproval: { type: boolean } automaticApprovalExtension: { type: boolean } automaticApprovalEmbedded: { type: boolean } - moduleClassName: { type: string } + moduleClassNames: + type: array + items: + type: string Type: type: string 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 7e9f0eed57..6ce8178d34 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 @@ -140,6 +140,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -861,7 +862,9 @@ public ApplicationForm getFormForVo(final Vo vo) throws FormNotExistsException { form.setAutomaticApproval(resultSet.getBoolean("automatic_approval")); form.setAutomaticApprovalExtension(resultSet.getBoolean("automatic_approval_extension")); form.setAutomaticApprovalEmbedded(resultSet.getBoolean("automatic_approval_embedded")); - form.setModuleClassName(resultSet.getString("module_name")); + if (resultSet.getString("module_names") != null) { + form.setModuleClassNames(Arrays.asList(resultSet.getString("module_names").split(","))); + } form.setVo(vo); return form; }, vo.getId()); @@ -885,7 +888,9 @@ public ApplicationForm getFormForGroup(final Group group) throws FormNotExistsEx form.setAutomaticApproval(resultSet.getBoolean("automatic_approval")); form.setAutomaticApprovalExtension(resultSet.getBoolean("automatic_approval_extension")); form.setAutomaticApprovalEmbedded(resultSet.getBoolean("automatic_approval_embedded")); - form.setModuleClassName(resultSet.getString("module_name")); + if (resultSet.getString("module_names") != null) { + form.setModuleClassNames(Arrays.asList(resultSet.getString("module_names").split(","))); + } form.setGroup(group); try { form.setVo(vosManager.getVoById(registrarSession, group.getVoId())); @@ -912,7 +917,9 @@ public ApplicationForm getFormById(PerunSession sess, int id) throws PrivilegeEx form1.setAutomaticApproval(resultSet.getBoolean("automatic_approval")); form1.setAutomaticApprovalExtension(resultSet.getBoolean("automatic_approval_extension")); form1.setAutomaticApprovalEmbedded(resultSet.getBoolean("automatic_approval_embedded")); - form1.setModuleClassName(resultSet.getString("module_name")); + if (resultSet.getString("module_names") != null) { + form1.setModuleClassNames(Arrays.asList(resultSet.getString("module_names").split(","))); + } try { form1.setVo(vosManager.getVoById(sess, resultSet.getInt("vo_id"))); } catch (Exception ex) { @@ -959,7 +966,9 @@ public ApplicationForm getFormByItemId(PerunSession sess, int id) throws Privile form1.setAutomaticApproval(resultSet.getBoolean("automatic_approval")); form1.setAutomaticApprovalExtension(resultSet.getBoolean("automatic_approval_extension")); form1.setAutomaticApprovalEmbedded(resultSet.getBoolean("automatic_approval_embedded")); - form1.setModuleClassName(resultSet.getString("module_name")); + if (resultSet.getString("module_names") != null) { + form1.setModuleClassNames(Arrays.asList(resultSet.getString("module_names").split(","))); + } try { form1.setVo(vosManager.getVoById(sess, resultSet.getInt("vo_id"))); } catch (Exception ex) { @@ -1251,8 +1260,8 @@ public int updateForm(PerunSession user, ApplicationForm form) throws PrivilegeE perun.getAuditer().log(user, new FormUpdated((form))); return jdbc.update( - "update application_form set automatic_approval=?, automatic_approval_extension=?, automatic_approval_embedded=?, module_name=? where id=?", - form.isAutomaticApproval(), form.isAutomaticApprovalExtension(), form.isAutomaticApprovalEmbedded(), form.getModuleClassName(), form.getId()); + "update application_form set automatic_approval=?, automatic_approval_extension=?, automatic_approval_embedded=?, module_names=? where id=?", + form.isAutomaticApproval(), form.isAutomaticApprovalExtension(), form.isAutomaticApprovalEmbedded(), String.join(",", form.getModuleClassNames()), form.getId()); } @Transactional @@ -1514,14 +1523,16 @@ public Application createApplicationInternal(PerunSession session, Application a } // call registrar module before auto validation so createAction is trigerred first - RegistrarModule module; + Set modules; if (application.getGroup() != null) { - module = getRegistrarModule(getFormForGroup(application.getGroup())); + modules = getRegistrarModules(getFormForGroup(application.getGroup())); } else { - module = getRegistrarModule(getFormForVo(application.getVo())); + modules = getRegistrarModules(getFormForVo(application.getVo())); } - if (module != null) { - module.createApplication(session, application, data); + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.createApplication(session, application, data); + } } } catch (ApplicationNotCreatedException ex) { @@ -1695,14 +1706,16 @@ public Application rejectApplication(PerunSession sess, int appId, String reason perun.getAuditer().log(sess, new ApplicationRejected(app)); // call registrar module - RegistrarModule module; + Set modules; if (app.getGroup() != null) { - module = getRegistrarModule(getFormForGroup(app.getGroup())); + modules = getRegistrarModules(getFormForGroup(app.getGroup())); } else { - module = getRegistrarModule(getFormForVo(app.getVo())); + modules = getRegistrarModules(getFormForVo(app.getVo())); } - if (module != null) { - module.rejectApplication(sess, app, reason); + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.rejectApplication(sess, app, reason); + } } // send mail @@ -1966,16 +1979,16 @@ public Application approveApplicationInternal(PerunSession sess, int appId) thro PerunPrincipal applicationPrincipal = new PerunPrincipal(app.getCreatedBy(), app.getExtSourceName(), app.getExtSourceType(), app.getExtSourceLoa(), additionalAttributes); // get registrar module - RegistrarModule module; + Set modules; if (app.getGroup() != null) { - module = getRegistrarModule(getFormForGroup(app.getGroup())); + modules = getRegistrarModules(getFormForGroup(app.getGroup())); } else { - module = getRegistrarModule(getFormForVo(app.getVo())); + modules = getRegistrarModules(getFormForVo(app.getVo())); } - - if (module != null) { - // call custom logic before approving - module.beforeApprove(sess, app); + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.beforeApprove(sess, app); + } } // mark as APPROVED @@ -2208,8 +2221,10 @@ public Application approveApplicationInternal(PerunSession sess, int appId) thro } // CONTINUE FOR BOTH APP TYPES - if (module != null) { - module.approveApplication(sess, app); + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.approveApplication(sess, app); + } } @@ -2241,16 +2256,16 @@ public void canBeApproved(PerunSession session, Application application) throws } // get registrar module - RegistrarModule module; + Set modules; if (application.getGroup() != null) { - module = getRegistrarModule(getFormForGroup(application.getGroup())); + modules = getRegistrarModules(getFormForGroup(application.getGroup())); } else { - module = getRegistrarModule(getFormForVo(application.getVo())); + modules = getRegistrarModules(getFormForVo(application.getVo())); } - - if (module != null) { - // call custom logic before approving - module.canBeApproved(session, application); + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.canBeApproved(session, application); + } } // generally for Group applications: @@ -2764,8 +2779,12 @@ public List getFormItemsWithPrefilledValu // throws exception if user couldn't submit application - no reason to get form checkDuplicateRegistrationAttempt(sess, appType, form); - RegistrarModule module = getRegistrarModule(form); - if (module != null) module.canBeSubmitted(sess, appType, federValues); + Set modules = getRegistrarModules(form);; + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.canBeSubmitted(sess , appType, federValues); + } + } // PROCEED Map parsedName = extractNames(federValues); @@ -2981,8 +3000,11 @@ public List getFormItemsWithPrefilledValu } } - if (module != null) { - module.processFormItemsWithData(sess, appType, form, itemsWithValues); + + if (!modules.isEmpty()) { + for (RegistrarModule module: modules) { + module.processFormItemsWithData(sess, appType, form, itemsWithValues); + } } if (!noPrefilledUneditableRequiredItems.isEmpty()) { @@ -4192,34 +4214,34 @@ private void setIfNotEmpty(Map map, String value, String key) { * @param form application form * @return RegistrarModule if present or null */ - private RegistrarModule getRegistrarModule(ApplicationForm form) { - + private Set getRegistrarModules(ApplicationForm form) { if (form == null) { // wrong input log.error("[REGISTRAR] Application form is null when getting it's registrar module."); throw new NullPointerException("Application form is null when getting it's registrar module."); } - if (form.getModuleClassName() != null && !form.getModuleClassName().trim().isEmpty()) { + Set modules = new LinkedHashSet<>(); + if (!form.getModuleClassNames().isEmpty()) { RegistrarModule module = null; - try { - log.debug("[REGISTRAR] Attempting to instantiate class: {}", MODULE_PACKAGE_PATH + form.getModuleClassName()); - module = (RegistrarModule) Class.forName(MODULE_PACKAGE_PATH + form.getModuleClassName()).newInstance(); - module.setRegistrar(registrarManager); - } catch (Exception ex) { - log.error("[REGISTRAR] Exception when instantiating module.", ex); - return module; + for (String regModule : form.getModuleClassNames()) { + try { + log.debug("[REGISTRAR] Attempting to instantiate class: {}{}", MODULE_PACKAGE_PATH, regModule); + module = (RegistrarModule) Class.forName(MODULE_PACKAGE_PATH + regModule) + .getDeclaredConstructor().newInstance(); + module.setRegistrar(registrarManager); + } catch (Exception ex) { + log.error("[REGISTRAR] Exception when instantiating module. Skipping module {}{}", + MODULE_PACKAGE_PATH, regModule, ex); + continue; + } + log.debug("[REGISTRAR] Class {}{} successfully created.", MODULE_PACKAGE_PATH, regModule); + modules.add(module); } - log.debug("[REGISTRAR] Class {} successfully created.", MODULE_PACKAGE_PATH + form.getModuleClassName()); - - return module; - } - - return null; - + return modules; } /** @@ -5251,7 +5273,7 @@ private static ResultSetExtractor> getPaginatedApplic private static final String APP_TYPE_SELECT = "select apptype from application_form_item_apptypes"; - private static final String FORM_SELECT = "select id,vo_id,group_id,automatic_approval,automatic_approval_extension,automatic_approval_embedded,module_name from application_form"; + private static final String FORM_SELECT = "select id,vo_id,group_id,automatic_approval,automatic_approval_extension,automatic_approval_embedded,module_names from application_form"; private static final String FORM_ITEM_SELECT = "select id,ordnum,shortname,required,type,fed_attr,src_attr,dst_attr,regex,hidden,disabled,hidden_dependency_item_id,disabled_dependency_item_id,updatable from application_form_items"; diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EscPithiaOrganizations.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EscPithiaOrganizations.java index 6b7f5ca426..71e15444c8 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EscPithiaOrganizations.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EscPithiaOrganizations.java @@ -31,7 +31,6 @@ import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.bl.PerunBl; import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; -import cz.metacentrum.perun.registrar.exceptions.FormNotExistsException; import cz.metacentrum.perun.registrar.exceptions.RegistrarException; import cz.metacentrum.perun.registrar.model.Application; import cz.metacentrum.perun.registrar.model.ApplicationForm; @@ -123,7 +122,7 @@ public Application approveApplication(PerunSession session, Application app) thr ApplicationForm newForm = null; try { newForm = registrar.getFormForGroup(organizationAdminsGroup); - newForm.setModuleClassName("EscPithiaOrganizationAdmins"); + newForm.setModuleClassNames(List.of("EscPithiaOrganizationAdmins")); registrar.updateForm(session, newForm); } catch (PerunException e) { throw new InternalErrorException("Can't set registration module to the admins groups!", e); 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 2333b03cb0..df671abf93 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 @@ -421,7 +421,7 @@ public Integer call(ApiCaller ac, Deserializer parms) throws PerunException { /*# * Updates the form attributes, not the form items. * - update automatic approval style - * - update module_name + * - update module_names * * @param form ApplicationForm Application form JSON object * @return ApplicationForm Updated application form or null when update failed diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/json/registrarManager/GetApplicationForm.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/json/registrarManager/GetApplicationForm.java index 153b1a9111..224e476361 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/json/registrarManager/GetApplicationForm.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/json/registrarManager/GetApplicationForm.java @@ -146,9 +146,9 @@ public void onClick(ClickEvent event) { ft.setHTML(1, 0, "EXTENSION: "); if (groupForm && hasEmbeddedGroupApplication) { ft.setHTML(2, 0, "EMBEDDED: "); - ft.setHTML(3, 0, "Module name: "); + ft.setHTML(3, 0, "Module names (comma separated list): "); } else { - ft.setHTML(2, 0, "Module name: "); + ft.setHTML(2, 0, "Module names (comma separated list): "); } @@ -156,7 +156,7 @@ public void onClick(ClickEvent event) { final ListBox lbInit = new ListBox(); final ListBox lbExt = new ListBox(); final ListBox lbEmbed = new ListBox(); - final TextBox className = new TextBox(); + final TextBox classNames = new TextBox(); lbInit.addItem("Automatic", "true"); lbInit.addItem("Manual", "false"); @@ -173,7 +173,7 @@ public void onClick(ClickEvent event) { } else { lbExt.setSelectedIndex(1); } - className.setText(form.getModuleClassName()); + classNames.setText(form.getModuleClassNames()); ft.setWidget(0, 1, lbInit); ft.setWidget(1, 1, lbExt); @@ -188,9 +188,9 @@ public void onClick(ClickEvent event) { } ft.setWidget(2, 1, lbEmbed); - ft.setWidget(3, 1, className); + ft.setWidget(3, 1, classNames); } else { - ft.setWidget(2, 1, className); + ft.setWidget(2, 1, classNames); } // click on save @@ -210,7 +210,7 @@ public void onFinished(JavaScriptObject jso) { if (groupForm && hasEmbeddedGroupApplication) { form.setAutomaticApprovalEmbedded(Boolean.parseBoolean(lbEmbed.getValue(lbEmbed.getSelectedIndex()))); } - form.setModuleClassName(className.getText().trim()); + form.setModuleClassNames(classNames.getText().trim()); request.updateForm(form); } }; @@ -223,7 +223,7 @@ public void onFinished(JavaScriptObject jso) { button.addClickHandler(ch); String appStyle = "Approval style: "; - String module = "
Module name: " + SafeHtmlUtils.fromString(form.getModuleClassName()).asString(); + String module = "
Module names: " + SafeHtmlUtils.fromString(form.getModuleClassNames()).asString(); if (form.getAutomaticApproval()==true) { appStyle = appStyle + "Automatic (INITIAL)"; @@ -279,7 +279,7 @@ public void onClick(ClickEvent event) { button.setEnabled(false); String appStyle = "Approval style: Form doesn't exists."; - String module = "
Module name: Form doesn't exists."; + String module = "
Module names: Form doesn't exists."; content.setHTML(0, 0, appStyle + module); content.setWidget(0, 1, button); diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/ApplicationForm.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/ApplicationForm.java index 15cac65319..44f71256ec 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/ApplicationForm.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/ApplicationForm.java @@ -45,9 +45,9 @@ public final native boolean getAutomaticApproval() /*-{ return this.automaticApproval; }-*/; - public final native String getModuleClassName() /*-{ - if (!this.moduleClassName) return ""; - return this.moduleClassName; + public final native String getModuleClassNames() /*-{ + if (!this.moduleClassNames) return ""; + return this.moduleClassNames.join(","); }-*/; /** @@ -92,8 +92,8 @@ public final native void setAutomaticApprovalEmbedded(boolean automatic) /*-{ this.automaticApprovalEmbedded = automatic; }-*/; - public final native void setModuleClassName(String name) /*-{ - this.moduleClassName = name; + public final native void setModuleClassNames(String names) /*-{ + this.moduleClassNames = names.split(","); }-*/; /** From 28d6f8777365fe96b6190400b841b9c802f91310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Fri, 1 Sep 2023 12:39:40 +0200 Subject: [PATCH 16/17] feat(core): new ExtSource type for IT4I - Used for group structure synchronization only. - Supports only lightweight sync! - Group members extsource must be set to "PERUN" SQL ext source as provided einfra logins from IT4I are converted to perun users IDs in a safe manner skipping any inconsistencies (e.g. anonymized accounts). This prevents unnecessary creation of new UserExtSource for all users plus we handle any possible inconsistencies in existing data. --- .../perun/core/impl/ExtSourceIT4I.java | 496 ++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 perun-core/src/main/java/cz/metacentrum/perun/core/impl/ExtSourceIT4I.java diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ExtSourceIT4I.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ExtSourceIT4I.java new file mode 100644 index 0000000000..5fdf0ea136 --- /dev/null +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ExtSourceIT4I.java @@ -0,0 +1,496 @@ +package cz.metacentrum.perun.core.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import cz.metacentrum.perun.core.api.ExtSource; +import cz.metacentrum.perun.core.api.ExtSourcesManager; +import cz.metacentrum.perun.core.api.GroupsManager; +import cz.metacentrum.perun.core.api.PerunClient; +import cz.metacentrum.perun.core.api.PerunPrincipal; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.UserExtSource; +import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExtSourceUnsupportedOperationException; +import cz.metacentrum.perun.core.api.exceptions.SubjectNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; +import cz.metacentrum.perun.core.blImpl.GroupsManagerBlImpl; +import cz.metacentrum.perun.core.implApi.ExtSourceSimpleApi; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.CookieStore; +import org.apache.http.client.HttpClient; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Implementation of ExtSource for IT4I SCS API. It is used to retrieve groups (projects) from IT4I + * including group members (list of users einfra logins). + * + * All synchronizations must be set to "lightweight" mode with fixed members ext source value to "PERUN" + * as IT4I doesn't provide full user data and Perun is source of truth in this case. + * + * @author Pavel Zlámal + */ +public class ExtSourceIT4I extends ExtSourceImpl implements ExtSourceSimpleApi { + + private final static Logger log = LoggerFactory.getLogger(ExtSourceIT4I.class); + + private final PerunPrincipal pp = new PerunPrincipal("perunSynchronizer", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + private PerunSession session = null; + + private final static String GROUP_PATH = "einfra-groups"; + private final static String USER_PATH = "einfra-group-members"; + + private String tokenUrl; + private String apiUrl; + private String username; + private String password; + + // this will allow us to keep session on all subsequent synchronization calls + private static CookieStore cookieStore = new BasicCookieStore(); + private static AccessToken accessToken = null; + + @Override + public List> findSubjectsLogins(String searchString) throws ExtSourceUnsupportedOperationException { + throw new ExtSourceUnsupportedOperationException(); + } + + @Override + public List> findSubjectsLogins(String searchString, int maxResults) throws ExtSourceUnsupportedOperationException { + throw new ExtSourceUnsupportedOperationException(); + } + + @Override + public Map getSubjectByLogin(String login) throws SubjectNotExistsException, ExtSourceUnsupportedOperationException { + throw new ExtSourceUnsupportedOperationException(); + } + + @Override + public List> getGroupSubjects(Map attributes) throws ExtSourceUnsupportedOperationException { + + init(); + + // Value in the filter has already been set when group structure was synchronized and group created + // Expected value supported by IT4I API is "group_id=?" where ? is replaced with groups login in "it4i" namespace. + String filterQuery = attributes.get(GroupsManager.GROUPMEMBERSQUERY_ATTRNAME); + String localFilter = (filterQuery != null) ? filterQuery : ""; + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + httpClientBuilder.setDefaultCookieStore(cookieStore); + HttpClient httpClient = httpClientBuilder.build(); + + HttpGet get = new HttpGet(apiUrl + USER_PATH + "?" + localFilter); + get.setHeader("Authorization", "Bearer "+getToken()); + + try { + List userLogins = httpClient.execute(get, new ResponseHandler>() { + @Override + public List handleResponse(HttpResponse httpResponse) throws ClientProtocolException, IOException { + + StatusLine statusLine = httpResponse.getStatusLine(); + if (statusLine.getStatusCode() >= 300) { + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + HttpEntity entity = httpResponse.getEntity(); + if (entity == null) { + throw new ClientProtocolException("Response contains no content"); + } + + ObjectMapper mapper = new ObjectMapper(); + CollectionType collectionType = new ObjectMapper().getTypeFactory().constructCollectionType(List.class, String.class); + + try (InputStream instream = entity.getContent()) { + return mapper.readValue(instream, collectionType); + } + + } + }); + + List> result = new ArrayList<>(); + + // return list of mappings with only logins, they will be mapped to existing UES based on the group members ExtSource by sync logic. + for (String userLogin : userLogins) { + Map map = new HashMap<>(); + + // convert external einfra login to perun user ID based on the user:def:login-namespace:einfra attribute value + User user = perunBl.getModulesUtilsBl().getUserByLoginInNamespace(session, userLogin, "einfra"); + if (user == null) { + log.warn("Subject with login '{}' skipped when retrieved from IT4I SCS since no related User was found in Perun with the same login!", userLogin); + continue; + } + // Check if proper UES exists and is mapped to the same user + String extLogin = String.valueOf(user.getId()); + ExtSource ex = null; + UserExtSource ues = null; + try { + ex = perunBl.getExtSourcesManagerBl().getExtSourceByName(session, ExtSourcesManager.EXTSOURCE_NAME_PERUN); + ues = perunBl.getUsersManagerBl().getUserExtSourceByExtLogin(session, ex, extLogin); + if (!Objects.equals(user.getId(), ues.getUserId())) { + log.warn("Subject with login '{}' skipped when retrieved from IT4I SCS since login value in Perun attribute is not mapped to the same User as existing UserExtSource in Perun.", userLogin); + continue; + } + + map.put("login", extLogin); + result.add(map); + + } catch (ExtSourceNotExistsException e) { + log.warn("Subject with login '{}' skipped when retrieved from IT4I SCS since there is no ExtSource named PERUN.", userLogin); + } catch (UserExtSourceNotExistsException e) { + log.warn("Subject with login '{}' skipped when retrieved from IT4I SCS since there is no UserExtSource with same login in Perun!", userLogin); + } + + } + + return result; + + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Override + public void close() throws ExtSourceUnsupportedOperationException { + // no-op + } + + @Override + public List> getSubjectGroups(Map attributes) throws ExtSourceUnsupportedOperationException { + + init(); + + List> result = new ArrayList<>(); + String filterQuery = attributes.get(GroupsManager.GROUPSQUERY_ATTRNAME); + String localFilter = (filterQuery != null) ? filterQuery : ""; + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + httpClientBuilder.setDefaultCookieStore(cookieStore); + HttpClient httpClient = httpClientBuilder.build(); + + HttpGet get = new HttpGet(apiUrl + GROUP_PATH + "?skip_members=true"); + get.setHeader("Authorization", "Bearer "+getToken()); + + ResponseHandler> rh = new ResponseHandler>() { + @Override + public List handleResponse(HttpResponse httpResponse) throws ClientProtocolException, IOException { + + StatusLine statusLine = httpResponse.getStatusLine(); + if (statusLine.getStatusCode() >= 300) { + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + HttpEntity entity = httpResponse.getEntity(); + if (entity == null) { + throw new ClientProtocolException("Response contains no content"); + } + + ObjectMapper mapper = new ObjectMapper(); + CollectionType collectionType = new ObjectMapper().getTypeFactory().constructCollectionType(List.class, IT4IGroup.class); + + try (InputStream instream = entity.getContent()) { + return mapper.readValue(instream, collectionType); + } + + } + }; + + + try { + + Map structure = it4iGroupsToStructure(httpClient.execute(get, rh)); + // remove all entries not starting with our filter + structure.keySet().stream().filter(s -> !s.startsWith(localFilter)).toList().forEach(structure.keySet()::remove); + + for (IT4IGroup group : structure.values()) { + Map map = new HashMap<>(); + map.put(GroupsManagerBlImpl.GROUP_LOGIN, group.getLogin()); + map.put(GroupsManagerBlImpl.GROUP_NAME, group.getGroup_name()); + map.put(GroupsManagerBlImpl.PARENT_GROUP_LOGIN,group.getParent_group_login()); + map.put(GroupsManagerBlImpl.GROUP_DESCRIPTION,group.getDescription()); + result.add(map); + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + + return result; + + } + + @Override + public List> getUsersSubjects() throws ExtSourceUnsupportedOperationException { + throw new ExtSourceUnsupportedOperationException(); + } + + /** + * Initialize ExtSource by its configuration + */ + private void init() { + tokenUrl = getAttributes().get("tokenUrl"); + apiUrl = getAttributes().get("apiUrl"); + username = getAttributes().get("username"); + password = getAttributes().get("password"); + if (this.session == null) { + this.session = perunBl.getPerunSession(pp, new PerunClient()); + } + } + + /** + * Converts list of all IT4I group to the structure based on the group login and parent login. + * Logins converted to the perun group naming format "top_group:sub_group:sub_sub_group" are used as keys in the mapping. + * + * @param it4IGroups List of groups retrieved from SCS + * @return Mapping of full group logins to the group + */ + private Map it4iGroupsToStructure(List it4IGroups) { + + Map result = new HashMap<>(); + buildGroupsStructure(result, "", null, it4IGroups); + return result; + + } + + /** + * Recursively build the IT4IGroups structure from the list of groups. + * + * @param structure structure to put resolved groups to + * @param prefix complete group prefix updated during recursive call like "top:", "top:parent:", .... + * @param parentLogin current parent group login + * @param groups groups to process + */ + private void buildGroupsStructure(Map structure, String prefix, String parentLogin, List groups) { + + for (IT4IGroup group : groups) { + + String login = group.getLogin(); + String groupParentLogin = group.getParent_group_login(); + + if (Objects.equals(parentLogin, groupParentLogin)) { + structure.put(prefix + login, group); + buildGroupsStructure(structure, prefix + login+":", login, groups); + } + + } + + } + + /** + * Retrieves access token value from the IT4I SCS API token endpoint. + * It re-uses value of existing valid token or replaces it with the new one. + * + * @return access token value + */ + private synchronized String getToken() { + + if (accessToken != null && accessToken.isValid()) { + return accessToken.getAccess_token(); + } + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + httpClientBuilder.setDefaultCookieStore(cookieStore); + HttpClient httpClient = httpClientBuilder.build(); + + NameValuePair clientId = new BasicNameValuePair("client_id", username); + NameValuePair clientSecret = new BasicNameValuePair("client_secret", password); + NameValuePair grantType = new BasicNameValuePair("grant_type", "client_credentials"); + + HttpEntity body = new UrlEncodedFormEntity(Arrays.asList(clientId,clientSecret,grantType), StandardCharsets.UTF_8); + HttpPost post = new HttpPost(tokenUrl); + post.setEntity(body); + ResponseHandler rh = new ResponseHandler() { + @Override + public AccessToken handleResponse(HttpResponse httpResponse) throws IOException { + + StatusLine statusLine = httpResponse.getStatusLine(); + if (statusLine.getStatusCode() >= 300) { + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + HttpEntity entity = httpResponse.getEntity(); + if (entity == null) { + throw new ClientProtocolException("Response contains no content"); + } + try (InputStream instream = entity.getContent()) { + return new ObjectMapper().readValue(instream, AccessToken.class); + } + + } + }; + + try { + accessToken = httpClient.execute(post, rh); + log.trace("{}", accessToken); + return accessToken.getAccess_token(); + } catch (IOException e) { + log.error("Couldn't contact token endpoint.", e); + return null; + } + + } + + /** + * Representation of Group returned from IT4I SCS API endpoint. + */ + static class IT4IGroup implements Comparable { + + private String login; + private String parent_group_login; + private String group_name; + private String description; + private List members; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getParent_group_login() { + return parent_group_login; + } + + public void setParent_group_login(String parent_group_login) { + this.parent_group_login = parent_group_login; + } + + public String getGroup_name() { + return group_name; + } + + public void setGroup_name(String group_name) { + this.group_name = group_name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getMembers() { + return members; + } + + public void setMembers(List members) { + this.members = members; + } + + @Override + public int compareTo(ExtSourceIT4I.IT4IGroup o) { + return login.compareTo(o.getLogin()); + } + + } + + /** + * Class represent access token returned from the IT4I SCS API token endpoint. + * We locally store time of creation to determine validity later on. + */ + static class AccessToken { + @JsonIgnore + private final Instant createdIn; + private String access_token; + private long expires_in; + private long refresh_expires_in; + private String token_type; + @JsonProperty(value = "not-before-policy") + private long not_before_policy; + private String scope; + + public AccessToken() { + createdIn=Instant.now(); + } + + /** + * Return actual access token value which can be used when calling SCS IT4I API. + * @return access token value + */ + public String getAccess_token() { + return access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public long getExpires_in() { + return expires_in; + } + + public void setExpires_in(long expires_in) { + this.expires_in = expires_in; + } + + public long getRefresh_expires_in() { + return refresh_expires_in; + } + + public void setRefresh_expires_in(long refresh_expires_in) { + this.refresh_expires_in = refresh_expires_in; + } + + public String getToken_type() { + return token_type; + } + + public void setToken_type(String token_type) { + this.token_type = token_type; + } + + public long getNot_before_policy() { + return not_before_policy; + } + + public void setNot_before_policy(long not_before_policy) { + this.not_before_policy = not_before_policy; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + /** + * Check access token validity based on the time of creation and expiration. + * + * @return TRUE if access token is still valid + */ + public boolean isValid() { + return Instant.now().isBefore(createdIn.plus(getExpires_in(), ChronoUnit.SECONDS)); + } + + } + +} From 025169d2a43745120fda8c8556b88d5d906f6f7d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 15:42:30 +0000 Subject: [PATCH 17/17] chore(deps): update dependency @semantic-release/github to v9.0.7 --- package-lock.json | 58 +++++++++++++++++++++++++++++++++++------------ package.json | 2 +- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index a89cbd3008..71228bd870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.6", + "@semantic-release/github": "9.0.7", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6", @@ -592,12 +592,12 @@ "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz", - "integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz", + "integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0" + "@octokit/types": "^12.0.0" }, "engines": { "node": ">= 18" @@ -606,6 +606,21 @@ "@octokit/core": ">=5" } }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^19.0.0" + } + }, "node_modules/@octokit/plugin-retry": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz", @@ -624,12 +639,12 @@ } }, "node_modules/@octokit/plugin-throttling": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz", - "integrity": "sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.0.0.tgz", + "integrity": "sha512-OkMbHYUidj81q92YRkPzWmwXkEtsI3KOcSkNm763aqUOh9IEplyX05XjKAdZFANAvaYH0Q4JBZwu4h2VnPVXZA==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -639,6 +654,21 @@ "@octokit/core": "^5.0.0" } }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==", + "dev": true + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^19.0.0" + } + }, "node_modules/@octokit/request": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz", @@ -873,15 +903,15 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.6.tgz", - "integrity": "sha512-GBGt9c3c2UdSvso4jcyQQSUpZA9hbfHqGQerZKN9WvVzCIkaBy8xkhOyiFVX08LjRHHT/H221SJNBLtuihX5iw==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.7.tgz", + "integrity": "sha512-SU3ayJ4/0TeIVyfCMLmuKoa4KvLclarPCmwY/zippm7sK95SwgWoFd8aFfAJIPGCRYnP3rfHRdYzphsrrNI3Cg==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^8.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/plugin-retry": "^6.0.0", - "@octokit/plugin-throttling": "^7.0.0", + "@octokit/plugin-throttling": "^8.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", diff --git a/package.json b/package.json index 7647a72ed6..0fe7ff9499 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "@semantic-release/github": "9.0.6", + "@semantic-release/github": "9.0.7", "commitizen": "4.3.0", "conventional-changelog-conventionalcommits": "6.1.0", "inquirer": "8.2.6",