From 32bbba58c9d13eca4ae1a316ad69de7ee3a0c16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Fri, 30 Jun 2023 19:33:20 +0200 Subject: [PATCH 01/30] refactor(core): removed unused registration modules DEPLOYMENT NOTE: Make sure following registration modules are not used on your instance - Ceitec, EduGain, Elixircz, Sitola and WeNMR. --- .../perun/registrar/modules/Ceitec.java | 49 ---------- .../perun/registrar/modules/EduGain.java | 48 ---------- .../perun/registrar/modules/Elixircz.java | 70 -------------- .../perun/registrar/modules/Sitola.java | 92 ------------------- .../perun/registrar/modules/WeNMR.java | 66 ------------- 5 files changed, 325 deletions(-) delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java delete mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java deleted file mode 100644 index 64a3c41728..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java +++ /dev/null @@ -1,49 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.exceptions.PerunException; -import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; -import cz.metacentrum.perun.registrar.model.Application; -import cz.metacentrum.perun.registrar.model.ApplicationFormItemData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Objects; - -/** - * Module for CEITEC VO at MU instance of Perun. - * - * The module check if name provided by User and IdP is different. If so, automatic approval is cancelled - * and VO manager must approve it manually. - * - * @author Pavel Zlámal - */ -public class Ceitec extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Ceitec.class); - - @Override - public void canBeApproved(PerunSession session, Application app) throws PerunException { - - List data = registrar.getApplicationDataById(session, app.getId()); - - String name = ""; - String fed_name = ""; - - for (ApplicationFormItemData item : data) { - if (Objects.equals(item.getShortname(),"jmeno")) { - name = item.getValue(); - } - if (Objects.equals(item.getShortname(),"jmeno_fed")) { - fed_name = item.getValue(); - } - } - - if (!Objects.equals(name,fed_name)) { - throw new CantBeApprovedException("Users name provided by IdP and User differ. Please check for correct name before approval.","","","",true, app.getId()); - } - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java deleted file mode 100644 index 9407f55920..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java +++ /dev/null @@ -1,48 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.*; -import cz.metacentrum.perun.core.api.exceptions.AlreadyAdminException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; -import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Application module for EduGain purpose - */ -public class EduGain extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Metacentrum.class); - - /** - * All new members will be given role VOOBSERVER and TOPGROUPCREATOR - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws UserNotExistsException, PrivilegeException, AlreadyAdminException, GroupNotExistsException, VoNotExistsException { - - if (Application.AppType.INITIAL.equals(app.getType())) { - - Vo vo = app.getVo(); - User user = app.getUser(); - - try { - AuthzResolver.setRole(session, user, vo, Role.TOPGROUPCREATOR); - - Group membersGroup = session.getPerun().getGroupsManager().getGroupByName(session, vo, "members"); - AuthzResolver.setRole(session, user, membersGroup, Role.GROUPADMIN); - } catch (RoleCannotBeManagedException e) { - throw new InternalErrorException(e); - } - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java deleted file mode 100644 index 6905bad2aa..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java +++ /dev/null @@ -1,70 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Objects; - -/** - * Module for ELIXIR-CZ VO at CESNET instance of Perun. - * - * By default all VO members get nearest 1.12. (loa 2) or +3m without possibility of extension. - * - * 1. For new VO registrations, if loa=2, manually set 1.1.9999 - * 2. For new Group registration, use VO rules - * 3. For VO extension, user VO rules - * - * @author Pavel Zlámal - */ -public class Elixircz extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Elixircz.class); - - @Override - public Application approveApplication(PerunSession session, Application app) throws MemberNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, WrongAttributeValueException, WrongReferenceAttributeValueException, ExtendMembershipException { - - PerunBl perun = (PerunBl) session.getPerun(); - Member member = perun.getMembersManagerBl().getMemberByUser(session, app.getVo(), app.getUser()); - - if (app.getGroup() == null && Objects.equals(app.getType(), Application.AppType.INITIAL)) { - - // IF VO INITIAL override VO rules to set unlimited (only to those with LoA = 2). - Attribute loaAttr = perun.getAttributesManagerBl().getAttribute(session, app.getUser(), AttributesManager.NS_USER_ATTR_VIRT + ":loa"); - int loa = Integer.valueOf((String) loaAttr.getValue()); - - if (loa == 2) { - Attribute attr = perun.getAttributesManagerBl().getAttribute(session, member, AttributesManager.NS_MEMBER_ATTR_DEF + ":membershipExpiration"); - attr.setValue("9999-01-01"); // set distant future as never expires - perun.getAttributesManagerBl().setAttribute(session, member, attr); - } - - } - - if ((app.getGroup() != null && Objects.equals(app.getType(), Application.AppType.INITIAL)) || - (app.getGroup() != null && Objects.equals(app.getType(), Application.AppType.EMBEDDED)) || - (app.getGroup() == null && Objects.equals(app.getType(), Application.AppType.EXTENSION))) { - - // GROUP INITIAL/EMBEDDED OR VO EXTENSION -> set back standard expiration date based on VO rules - Attribute attr = perun.getAttributesManagerBl().getAttribute(session, member, AttributesManager.NS_MEMBER_ATTR_DEF + ":membershipExpiration"); - perun.getAttributesManagerBl().removeAttribute(session, member, attr); - perun.getMembersManagerBl().extendMembership(session, member); - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java deleted file mode 100644 index 13b64758c3..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Module for VO Sitola - * - * @author Pavel Zlamal <256627@mail.muni.cz> - */ -public class Sitola extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Sitola.class); - - /** - * All new Sitola members will have MU eduroam identity added if they posses MU login. - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws WrongAttributeAssignmentException, UserNotExistsException, AttributeNotExistsException, PrivilegeException, WrongAttributeValueException, WrongReferenceAttributeValueException { - - // get perun from session - PerunBl perun = (PerunBl) session.getPerun(); - User user = app.getUser(); - - if (user != null) { - - Attribute eduroamIdentities = perun.getAttributesManagerBl().getAttribute(session, user, "urn:perun:user:attribute-def:def:eduroamIdentities"); - Attribute loginMu = perun.getAttributesManagerBl().getAttribute(session, user, "urn:perun:user:attribute-def:def:login-namespace:mu"); - - if (eduroamIdentities.getValue() == null) { - - if (loginMu.getValue() != null) { - - // add MU identity - List identities = new ArrayList<>(); - identities.add(loginMu.getValue() +"@eduroam.muni.cz"); - - eduroamIdentities.setValue(identities); - - // use Bl since VO manager normally can't set this attribute - perun.getAttributesManagerBl().setAttribute(session, user, eduroamIdentities); - - } - - } else { - - if (loginMu.getValue() != null) { - - // check if not already present and set - boolean found = false; - for (String value : eduroamIdentities.valueAsList()) { - if (Objects.equals(value, loginMu.getValue() +"@eduroam.muni.cz")) { - found = true; - break; - } - } - - if (!found) { - // add MU eduroam identity - ((List) eduroamIdentities.valueAsList()).add(loginMu.getValue() +"@eduroam.muni.cz"); - // use Bl since VO manager normally can't set this attribute - perun.getAttributesManagerBl().setAttribute(session, user, eduroamIdentities); - } - - } - - } - - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java deleted file mode 100644 index b18a09f952..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java +++ /dev/null @@ -1,66 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -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.PerunException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Objects; - -/** - * Module for WeNMR VO at CESNET instance of Perun. - * - * The module - * 1. Allows (auto)approval of applications submitted by users with IdP identity https://www.structuralbiology.eu/idp/shibboleth - * 2. Enforce manual approval of applications submitted by users without IdP identity https://www.structuralbiology.eu/idp/shibboleth - * - * @author Pavel Zlámal - */ -public class WeNMR extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(WeNMR.class); - - @Override - public void canBeApproved(PerunSession session, Application app) throws PerunException { - - // check if submitted from trusted IdP - if (!Objects.equals("https://www.structuralbiology.eu/idp/shibboleth", app.getExtSourceName())) { - - // submitted by untrusted IdP - PerunBl perun = (PerunBl) session.getPerun(); - User user; - - // check if user is known - if (app.getUser() != null) { - user = app.getUser(); - } else { - try { - user = perun.getUsersManagerBl().getUserByExtSourceNameAndExtLogin(session, app.getExtSourceName(), app.getCreatedBy()); - } catch (Exception ex) { - // unable to find user -> untrusted IdP - throw new CantBeApprovedException("Application can't be approved automatically. User doesn't have identity from \"www.structuralbiology.eu\". Please check users identity before manual/force approval.", "", "", "", true, app.getId()); - } - } - - List ueses = perun.getUsersManagerBl().getUserExtSources(session, user); - for (UserExtSource ues : ueses) { - if (Objects.equals("https://www.structuralbiology.eu/idp/shibboleth", ues.getExtSource().getName())) { - // user has trusted identity - return; - } - } - throw new CantBeApprovedException("Application can't be approved automatically. User doesn't have identity from \"www.structuralbiology.eu\". Please check users identity before manual/force approval.", "", "", "", true, app.getId()); - - } - - // submitted from trusted IdP - - } - -} From ab92cc4296a03ee8ae443f073479ec7342d02dcf Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Wed, 5 Jul 2023 16:54:35 +0200 Subject: [PATCH 02/30] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Generate=20addition?= =?UTF-8?q?alIdentifier=20in=20LSHoste=20(and=20acc)=20UESes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GEANTs' proxy identifies user by additional identifiers. If no such identifier is set into the attribute, proxy cannot lookup user, until the UserExtSource is updated (e.g. via Perun GUI login). --- .../modules/LifeScienceHostelRI.java | 25 ++++++++++++++++++- .../modules/LifeScienceHostelRIAcc.java | 23 +++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java index 5aa274fbc4..fd944d87b9 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java @@ -17,6 +17,7 @@ import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; @@ -27,6 +28,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + /** * Module for VO lifescience_hostel on LifeScience Perun machine * @@ -43,6 +47,7 @@ public class LifeScienceHostelRI extends DefaultRegistrarModule { private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; private final static String VO_SHORTNAME = "lifescience"; + private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; /** * Create proper UserExtSource @@ -65,11 +70,29 @@ public Application approveApplication(PerunSession session, Application app) thr ues.setLoa(0); try { - perun.getUsersManagerBl().addUserExtSource(session, user, ues); + ues = perun.getUsersManagerBl().addUserExtSource(session, user, ues); } catch (UserExtSourceExistsException ex) { // this is OK } + try { + Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); + List attrValue = new ArrayList<>(); + if (auidsAttr.getValue() != null + && auidsAttr.valueAsList() != null + && !auidsAttr.valueAsList().isEmpty() + ) { + attrValue = auidsAttr.valueAsList(); + } + auidsAttr.valueAsList().add(login + LS_HOSTEL_SCOPE); + auidsAttr.setValue(attrValue); + perun.getAttributesManager().setAttribute(session, ues, auidsAttr); + } catch (UserExtSourceNotExistsException e) { + // should not happen + } catch (AttributeNotExistsException e) { + // ok, attribute is probably not used + } + } if (Application.AppType.INITIAL.equals(app.getType())) { diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java index 0e6ab033eb..1b0b6ba439 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java @@ -17,6 +17,7 @@ import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; @@ -27,6 +28,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + /** * Module for VO lifescience_hostel on LifeScience acceptance Perun machine * @@ -43,6 +47,7 @@ public class LifeScienceHostelRIAcc extends DefaultRegistrarModule { private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; private final static String VO_SHORTNAME = "lifescience"; + private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; /** * Create proper UserExtSource @@ -70,6 +75,24 @@ public Application approveApplication(PerunSession session, Application app) thr // this is OK } + try { + Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); + List attrValue = new ArrayList<>(); + if (auidsAttr.getValue() != null + && auidsAttr.valueAsList() != null + && !auidsAttr.valueAsList().isEmpty() + ) { + attrValue = auidsAttr.valueAsList(); + } + auidsAttr.valueAsList().add(login + LS_HOSTEL_SCOPE); + auidsAttr.setValue(attrValue); + perun.getAttributesManager().setAttribute(session, ues, auidsAttr); + } catch (UserExtSourceNotExistsException e) { + // should not happen + } catch (AttributeNotExistsException e) { + // ok, attribute is probably not used + } + } if (Application.AppType.INITIAL.equals(app.getType())) { From f2320023e2e1487152bfd7550503cc45a43e58a5 Mon Sep 17 00:00:00 2001 From: mattjoke Date: Wed, 19 Jul 2023 10:33:44 +0200 Subject: [PATCH 03/30] fix(cli): fixed encoding * setValueFromArray and getValueAsScalar now escape to/from UTF-8 * Dumper uses Unicode sequence and therefore it is replaced --- perun-cli/Perun/beans/Attribute.pm | 30 +++++++++++++-------------- perun-cli/setAttributeCriticalActions | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/perun-cli/Perun/beans/Attribute.pm b/perun-cli/Perun/beans/Attribute.pm index 0890ee61ff..45dd36950c 100644 --- a/perun-cli/Perun/beans/Attribute.pm +++ b/perun-cli/Perun/beans/Attribute.pm @@ -3,7 +3,6 @@ package Perun::beans::Attribute; use strict; use warnings; use Switch; -use Data::Dumper; use overload '""' => \&toString; @@ -269,19 +268,13 @@ sub getValueAsScalar { case "SCALAR" { return $value } case "ARRAY" { return '["'.join('", "', @$value).'"]' } case "HASH" { - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Indent = 0; - local $Data::Dumper::Useqq = 1; - - { - no warnings 'redefine'; - sub Data::Dumper::qquote { - my $s = shift; - return "'$s'"; - } + my $str = '{'; + foreach my $key (reverse keys %$value) { + $str .= '"'.$key.'" => "'.$value->{$key}.'",'; } - - return Dumper($value); + $str =~ s/,$//; + $str .= '}'; + return $str; } case "JSON::XS::Boolean" { return ($value) ? 'true' : 'false'; @@ -322,11 +315,18 @@ sub setValueFromArray { } } case /^array$/ { - $attribute->setValue( \@_ ); + my @arr = @_; + for (my $i=0; $isetValue( \@arr ); } case "hash" { my %hash = @_; - $attribute->setValue( \%hash ); + for my $key (keys %hash) { + utf8::decode($hash{$key}); + } + $attribute->setValue( \%hash ); } else { die "Unknown attribute type. Type=".$attribute->getType; diff --git a/perun-cli/setAttributeCriticalActions b/perun-cli/setAttributeCriticalActions index e725ebf98a..150dd06dc9 100755 --- a/perun-cli/setAttributeCriticalActions +++ b/perun-cli/setAttributeCriticalActions @@ -6,7 +6,6 @@ use Getopt::Long qw(:config no_ignore_case); use Perun::Agent; use Perun::Common qw(printMessage); use Switch; -use Data::Dumper; sub help { return qq{ From 56d672243a6b1ebe2733bcc5e153c5af6fcad11e Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Tue, 18 Jul 2023 22:18:10 +0200 Subject: [PATCH 04/30] feat(core): last successful propagation Added info about the last time when propagation was successfully done. --- .../perun/core/api/RichDestination.java | 26 ++++++++++-- .../perun/core/impl/ServicesManagerImpl.java | 29 ++++++++++++- .../ServicesManagerEntryIntegrationTest.java | 41 ++++++++++++++++++- perun-openapi/openapi.yml | 1 + 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java index cc627156d7..fe0bc71576 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java @@ -1,8 +1,6 @@ package cz.metacentrum.perun.core.api; -import cz.metacentrum.perun.core.api.Service; -import java.util.List; -import cz.metacentrum.perun.core.api.BeansUtils; +import java.sql.Timestamp; /** * Destination where services are propagated. @@ -12,6 +10,7 @@ public class RichDestination extends Destination implements Comparable userExtSources, List userAttributes, List memberAttributes) { this(user, member, userExtSources); this.userAttributes = userAttributes; @@ -60,6 +70,14 @@ public void setBlocked(boolean blocked) { this.blocked = blocked; } + public Timestamp getLastSuccessfulPropagation() { + return lastSuccessfulPropagation; + } + + public void setLastSuccessfulPropagation(Timestamp lastSuccessfulPropagation) { + this.lastSuccessfulPropagation = lastSuccessfulPropagation; + } + @Override public int hashCode() { final int prime = 31; @@ -109,6 +127,7 @@ public String serializeToString() { ", facility=<").append(getFacility() == null ? "\\0" : getFacility().serializeToString()).append(">").append( ", service=<").append(getService() == null ? "\\0" : getService().serializeToString()).append(">").append( ", blocked=<").append(isBlocked()).append(">").append( + ", lastSuccessfulPropagation=<").append(getLastSuccessfulPropagation()).append(">").append( ']').toString(); } @@ -123,6 +142,7 @@ public String toString() { ).append("', facility='").append(getFacility() ).append("', service='").append(getService() ).append("', blocked='").append(isBlocked() + ).append("', lastSuccessfulPropagation='").append(getLastSuccessfulPropagation() ).append("']").toString(); } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java index adfb0fbf6f..9563adffe2 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java @@ -37,6 +37,7 @@ import javax.sql.DataSource; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -93,6 +94,15 @@ public ServicesManagerImpl(DataSource perunPool) { serviceDenialMappingSelectQuery + ", " + "facility_service_destinations.propagation_type as f_s_des_propagation_type "; + public final static String richDestinationWithLastSuccessfulPropagationMappingSelectQuery = " " + destinationMappingSelectQuery + ", " + + "facilities.id as facilities_id, facilities.name as facilities_name, facilities.dsc as facilities_dsc, " + + "facilities.created_at as facilities_created_at, facilities.created_by as facilities_created_by, facilities.modified_at as facilities_modified_at, facilities.modified_by as facilities_modified_by, " + + "facilities.modified_by_uid as facilities_modified_by_uid, facilities.created_by_uid as facilities_created_by_uid, " + + serviceMappingSelectQuery + ", " + + serviceDenialMappingSelectQuery + ", " + + "facility_service_destinations.propagation_type as f_s_des_propagation_type, " + + "last_success.success_at as success_at "; + public static final RowMapper SERVICE_MAPPER = (resultSet, i) -> { Service service = new Service(); service.setId(resultSet.getInt("services_id")); @@ -212,7 +222,15 @@ public ServicesManagerImpl(DataSource perunPool) { ServiceDenial serviceDenial = SERVICE_DENIAL_MAPPER.mapRow(resultSet, i); - return new RichDestination(destination, facility, service, serviceDenial != null); + // if success_at column is missing in results, use null value + Timestamp lastSuccessfulPropagation; + try { + lastSuccessfulPropagation = resultSet.getTimestamp("success_at"); + } catch (SQLException ex) { + lastSuccessfulPropagation = null; + } + + return new RichDestination(destination, facility, service, serviceDenial != null, lastSuccessfulPropagation); }; @SuppressWarnings("ConstantConditions") @@ -750,12 +768,19 @@ public List getDestinations(PerunSession perunSession, Facility fac @Override public List getAllRichDestinations(PerunSession perunSession, Facility facility) { try { - return jdbc.query("select " + richDestinationMappingSelectQuery + " from facility_service_destinations " + + return jdbc.query("select " + richDestinationWithLastSuccessfulPropagationMappingSelectQuery + " from facility_service_destinations " + "join destinations on destinations.id=facility_service_destinations.destination_id " + "join services on services.id=facility_service_destinations.service_id " + "join facilities on facilities.id=facility_service_destinations.facility_id " + "left join service_denials on services.id = service_denials.service_id and " + " destinations.id = service_denials.destination_id " + + + "left join (select destination_id, services.id as service_id, facilities.id as facility_id, max(timestamp) as success_at from tasks_results " + + "join services on services.id = (select service_id from tasks where id = tasks_results.task_id) " + + "join facilities on facilities.id = (select facility_id from tasks where id = tasks_results.task_id) " + + "where status = 'DONE' group by destination_id, services.id, facilities.id) as last_success " + + " on last_success.destination_id = facility_service_destinations.destination_id and last_success.service_id = services.id and last_success.facility_id = facilities.id " + + "where facility_service_destinations.facility_id=? order by destinations.destination", RICH_DESTINATION_MAPPER, facility.getId()); } catch (RuntimeException e) { throw new InternalErrorException(e); diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java index a79401ba40..2ff13ff890 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java @@ -42,9 +42,12 @@ import cz.metacentrum.perun.core.api.exceptions.ServicesPackageExistsException; import cz.metacentrum.perun.core.api.exceptions.ServicesPackageNotExistsException; import cz.metacentrum.perun.core.impl.AuthzRoles; +import cz.metacentrum.perun.taskslib.model.Task; +import cz.metacentrum.perun.taskslib.model.TaskResult; import org.assertj.core.api.Assertions; import org.junit.Test; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -56,7 +59,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; /** @@ -1271,6 +1273,43 @@ public void getAllRichDestinationsWithFacility() throws Exception { assertTrue("there is the right destination in the richDestination",richDestination.getDestination().equals(destination.getDestination())); } + @Test + public void getAllRichDestinationsAndTheirLastSuccessfulPropagation() throws Exception { + System.out.println(CLASS_NAME + "getAllRichDestinationsAndTheirLastSuccessfulPropagation"); + + Timestamp success_at = new Timestamp(System.currentTimeMillis()); + + service = setUpService(); + facility = setUpFacility(); + destination = setUpDestination(); + perun.getServicesManagerBl().addDestination(sess, service, facility, destination); + + // Task + Task task = new Task(); + task.setFacility(facility); + task.setService(service); + task.setSchedule(0L); + task.setStatus(Task.TaskStatus.WARNING); + task.setDestinations(List.of(destination)); + task.setId(perun.getTasksManagerBl().insertTask(sess, task)); + + // Task result + TaskResult result = new TaskResult(); + result.setDestination(destination); + result.setDestinationId(destination.getId()); + result.setService(service); + result.setTaskId(task.getId()); + result.setStatus(TaskResult.TaskResultStatus.DONE); + result.setTimestamp(success_at); + result.setId(perun.getTasksManagerBl().insertNewTaskResult(sess, result)); + + List richDestinations = perun.getServicesManager().getAllRichDestinations(sess, facility); + assertTrue("There should be one destination",richDestinations.size() == 1); + + RichDestination richDestination = richDestinations.get(0); + assertEquals(success_at.getTime() / 1000, richDestination.getLastSuccessfulPropagation().getTime() / 1000); + } + @Test public void getAllRichDestinationsWithService() throws Exception { System.out.println(CLASS_NAME + "getAllRichDestinationsWithService"); diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index cd5aaf6d2f..ff7b210f67 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -1003,6 +1003,7 @@ components: blocked: { type: boolean } service: { $ref: '#/components/schemas/Service' } facility: { $ref: '#/components/schemas/Facility' } + lastSuccessfulPropagation: { type: string } discriminator: propertyName: beanName From d5602ac2029ace11afeb898a5ff75dd35e3d5f49 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:55:54 +0000 Subject: [PATCH 05/30] fix(deps): update dependency org.springframework.boot:spring-boot-starter-parent to v2.7.14 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index abdc6d91fa..06ce49bfdb 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.13 + 2.7.14 From 0372a05c6df38b2fa52b0bb8b7e666826231df9f Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Mon, 17 Jul 2023 11:03:43 +0200 Subject: [PATCH 06/30] fix(core): filter get rich users with attributes Filter out users to which the user does not have a correct privilege. --- perun-base/src/main/resources/perun-roles.yml | 16 ++++++++++ .../perun/core/entry/UsersManagerEntry.java | 31 ++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 4499dd94f6..49812e3cb3 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -6506,6 +6506,22 @@ perun_policies: include_policies: - default_policy + filter-findRichUsersWithAttributes_policy: + policy_roles: + - SECURITYADMIN: + - FACILITYADMIN: + - FACILITYOBSERVER: + - RESOURCEADMIN: + - RESOURCEOBSERVER: + - GROUPADMIN: + - GROUPOBSERVER: + - GROUPMEMBERSHIPMANAGER: + - VOADMIN: + - VOOBSERVER: + - PERUNOBSERVER: + include_policies: + - default_policy + findRichUsersWithAttributesByExactMatch_String_List_policy: policy_roles: - PERUNOBSERVER: diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java index 7576dc84d5..2d7a4eaba5 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java @@ -1425,8 +1425,37 @@ public List findRichUsersWithAttributes(PerunSession sess, String sear } } - return getPerunBl().getUsersManagerBl().filterOnlyAllowedAttributes(sess, getUsersManagerBl().findRichUsersWithAttributes(sess, searchString, attrNames)); + List users = getPerunBl().getUsersManagerBl().filterOnlyAllowedAttributes(sess, getUsersManagerBl().findRichUsersWithAttributes(sess, searchString, attrNames)); + return filterRichUsers(sess, users); + } + + /** + * Filters rich users to which the principal has access. + * + * @param sess session + * @param users list of all rich users + * @return filtered list of rich users + */ + private List filterRichUsers(PerunSession sess, List users) { + List result = new ArrayList<>(); + String filterPolicy = "filter-findRichUsersWithAttributes_policy"; + + for (RichUser user : users) { + List vos = perunBl.getUsersManagerBl().getVosWhereUserIsMember(sess, user); + List groups = perunBl.getGroupsManagerBl().getUserGroups(sess, user); + List facilities = perunBl.getFacilitiesManagerBl().getAssignedFacilities(sess, user); + List resources = perunBl.getUsersManagerBl().getAllowedResources(sess, user); + + if (AuthzResolver.authorizedInternal(sess, filterPolicy) || + vos.stream().anyMatch(vo -> AuthzResolver.authorizedInternal(sess, filterPolicy, vo)) || + groups.stream().anyMatch(group -> AuthzResolver.authorizedInternal(sess, filterPolicy, group)) || + facilities.stream().anyMatch(facility -> AuthzResolver.authorizedInternal(sess, filterPolicy, facility)) || + resources.stream().anyMatch(resource -> AuthzResolver.authorizedInternal(sess, filterPolicy, resource))) { + result.add(user); + } + } + return result; } @Override From d18e46f37c8f1e6d6c2c978ffc53a285f1d4bc83 Mon Sep 17 00:00:00 2001 From: mattjoke Date: Thu, 20 Jul 2023 15:01:14 +0200 Subject: [PATCH 07/30] fix: remove unused variable * fixed 'unused variable' in `parseRPCMethods.pl` --- .../parseRpcMethods.pl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl index 8800149fe2..70fa596e2e 100755 --- a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl +++ b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl @@ -93,13 +93,15 @@ $objectExamples{"List<RichGroup>"} = $listPrepend . $objectExamples{"RichGroup"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichGroup>"}; +$objectExamples{"User"} = "{ \"firstName\" : \"Some\" , \"lastName\" : \"Body\" , \"middleName\" : null , \"titleBefore\" : \"Mgr.\" , \"titleAfter\" : null , \"serviceUser\" : false , \"sponsoredUser\" : false , \"specificUser\" : false , \"majorSpecificType\" : \"NORMAL\" , \"id\" : 34 , \"uuid\" : \"5e5a02dd-f991-4706-a428-69c3ea6c5ce8\" , \"beanName\" : \"User\" }"; +$objectExamples{"List<User>"} = $listPrepend . $objectExamples{"User"} . $listAppend; +$objectExamples{"List"} = $objectExamples{"List<User>"}; + $objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; $objectExamples{"List<Application>"} = $listPrepend . $objectExamples{"Application"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Application>"}; -$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; -$objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; -$objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; +$objectExamples{"ApplicationForm"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"automaticApproval\" : true , \"automaticApprovalExtension\" : true , \"moduleClassName\" : \"cz.metacentrum.perun.core.impl.modules.attributes.VirtualAttributesModule\" , \"beanName\" : \"ApplicationForm\" }"; $objectExamples{"ApplicationFormItem"} = "{ \"id\" : 12 , \"shortname\" : \"Form item name\' , \"required\" : false , \"updatable\" : true , \"type\" : \"CHECKBOX\" , \"federationAttribute\" : \"\" , \"perunSourceAttribute\" : \"PERUNPEOPLE\" , \"perunDestinationAttribute\" : \"\", \"regex\" : \"\", \"applicationTypes\" : [\"INITIAL\" , \"EXTENSION\"], \"ordnum\" : 5, \"hiddenDependencyItemId\" : 5, \"disabledDependencyItemId\" : 5, \"disabled\" : \"NEVER\", \"hidden\" : \"NEVER\", \"beanName\" : \"ApplicationFormItem\" }"; $objectExamples{"List<ApplicationFormItem>"} = $listPrepend . $objectExamples{"ApplicationFormItem"} . $listAppend; @@ -109,6 +111,10 @@ $objectExamples{"List<ApplicationFormItemData>"} = $listPrepend . $objectExamples{"ApplicationFormItemData"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ApplicationFormItemData>"}; +$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; +$objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; +$objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; + $objectExamples{"ApplicationsPageQuery"} = "{ \"pageSize\" : 3 , \"offset\" : 0 , \"order\" : \"ASCENDING\" , \"sortColumn\" : \"ID\" , \"includeGroupApplications\" : true , \"searchString\" : \"Doe\" , \"states\" : [\"VERIFIED\" , \"NEW\"] , \"dateFrom\" : \"2011-05-17\", \"dateTo\" : \"2011-05-17\", \"memberId\" : 10 , \"groupId\" : 10 }"; $objectExamples{"List<ApplicationsPageQuery>"} = $listPrepend . $objectExamples{"ApplicationsPageQuery"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ApplicationsPageQuery>"}; @@ -120,10 +126,6 @@ $objectExamples{"List<Member>"} = $listPrepend . $objectExamples{"Member"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Member>"}; -$objectExamples{"User"} = "{ \"firstName\" : \"Some\" , \"lastName\" : \"Body\" , \"middleName\" : null , \"titleBefore\" : \"Mgr.\" , \"titleAfter\" : null , \"serviceUser\" : false , \"sponsoredUser\" : false , \"specificUser\" : false , \"majorSpecificType\" : \"NORMAL\" , \"id\" : 34 , \"uuid\" : \"5e5a02dd-f991-4706-a428-69c3ea6c5ce8\" , \"beanName\" : \"User\" }"; -$objectExamples{"List<User>"} = $listPrepend . $objectExamples{"User"} . $listAppend; -$objectExamples{"List"} = $objectExamples{"List<User>"}; - $objectExamples{"ExtSource"} = "{ \"name\" : \"PERUNPEOPLE\" , \"type\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"attributes\" : {} , \"id\" : 2 , \"beanName\" : \"ExtSource\" }"; $objectExamples{"List<ExtSource>"} = $listPrepend . $objectExamples{"ExtSource"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ExtSource>"}; @@ -272,7 +274,8 @@ $objectExamples{"List<AttributePolicy>"} = $listPrepend . $objectExamples{"AttributePolicy"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<AttributePolicy>"}; -$objectExamples{"AttributePolicyCollection"} = "{ \"id\" : 10 , \"attributeId\" : 2220 , \"action\" : \"READ\" , \"policies\" : " . $objectExamples{"List<AttributePolicyCollection>"} . " }"; +$objectExamples{"AttributePolicyCollection"} = "{ \"id\" : 10 , \"attributeId\" : 2220 , \"action\" : \"READ\" , \"policies\" : " . $objectExamples{"List<AttributePolicy>"} . " }"; + $objectExamples{"List<AttributePolicyCollection>"} = $listPrepend . $objectExamples{"AttributePolicyCollection"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<AttributePolicyCollection>"}; From 54aa615705fa40f04b70410aec04291213e813e1 Mon Sep 17 00:00:00 2001 From: Luboslav Halama Date: Mon, 24 Jul 2023 16:36:35 +0200 Subject: [PATCH 08/30] refactor(core): virt. attribute for eligibilities cleanup --- .../core/blImpl/AttributesManagerBlImpl.java | 2 +- ..._attribute_def_virt_userEligibilities.java | 39 ------------------- .../perun/ldapc/model/PerunAttribute.java | 2 +- 3 files changed, 2 insertions(+), 41 deletions(-) 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 0a436cd1b1..db70dd775b 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 @@ -7989,7 +7989,7 @@ protected void initialize() { attr = new AttributeDefinition(); attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); attr.setFriendlyName("userEligibilities"); - attr.setDisplayName("user Eligibilities"); + attr.setDisplayName("User eligibilities"); attr.setType(LinkedHashMap.class.getName()); attr.setDescription("Virtual attribute, which collects all eligibilities user ext source attributes " + "with keys and values (map). Only the highest value is selected for each key."); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java index 089334e145..1fbfd0a857 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java @@ -82,45 +82,6 @@ public Attribute getAttributeValue(PerunSessionImpl sess, User user, AttributeDe return attribute; } -// @Override -// public List getHandleIdentifiers() { -// List handleIdentifiers = super.getHandleIdentifiers(); -// handleIdentifiers.add(auditEvent -> { -// if (auditEvent instanceof AttributeChangedForUser && -// ((AttributeChangedForUser) auditEvent).getAttribute().getFriendlyName().equals(getDestinationAttributeFriendlyName())) { -// return ((AttributeChangedForUser) auditEvent).getUser().getId(); -// } else { -// return null; -// } -// }); -// return handleIdentifiers; -// } -// -// @Override -// public List resolveVirtualAttributeValueChange(PerunSessionImpl perunSession, AuditEvent message) throws AttributeNotExistsException, WrongAttributeAssignmentException, WrongReferenceAttributeValueException { -// List resolvingMessages = super.resolveVirtualAttributeValueChange(perunSession, message); -// if (message == null) return resolvingMessages; -// -// if (message instanceof AttributeSetForUser && -// ((AttributeSetForUser) message).getAttribute().getFriendlyName().equals(getSecondarySourceAttributeFriendlyName())) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeSetForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeSetForUser) message).getUser())); -// } else if (message instanceof AttributeChangedForUser) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeChangedForUser) message).getUser())); -// } else if (message instanceof AttributeRemovedForUser) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeRemovedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeRemovedForUser) message).getUser())); -// } -// -// return resolvingMessages; -// } - -// private AuditEvent resolveEvent(PerunSessionImpl perunSession, User user) throws AttributeNotExistsException { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// return new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), user); -// } - @Override public AttributeDefinition getAttributeDefinition() { AttributeDefinition attr = new AttributeDefinition(); diff --git a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java index 6e0b63a97a..2d6feeff47 100644 --- a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java +++ b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java @@ -74,7 +74,7 @@ public interface PerunAttributeNames { public static final String ldapAttrAdminOfGroup = "adminOfGroup"; public static final String ldapAttrAdminOfFacility = "adminOfFacility"; public static final String ldapAttrUuid = "uuid"; - public static final String ldapAttrUserEligibilities = "userEligibilities"; + public static final String ldapAttrUserEligibilities = perunAttrUserEligibilities; //LDAP OBJECT CLASSES public static final String objectClassTop = "top"; From 72bab7791da1982295ecb417bf6520e9c9f65f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ou=C5=A1ek?= Date: Tue, 25 Jul 2023 08:36:44 +0200 Subject: [PATCH 09/30] ci: generate release notes before adding to git should fix missing UPGRADE.md --- .releaserc.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.releaserc.json b/.releaserc.json index 0074a3daa2..24e63763b9 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -89,6 +89,13 @@ "prepareCmd": "./update-versions.sh ${nextRelease.version} && mvn -B clean install" } ], + [ + "@semantic-release/changelog", + { + "changelogFile": "UPGRADE.md", + "changelogTitle": "Upgrade notes" + } + ], [ "@semantic-release/git", { @@ -104,13 +111,6 @@ "message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}" } ], - [ - "@semantic-release/changelog", - { - "changelogFile": "UPGRADE.md", - "changelogTitle": "Upgrade notes" - } - ], [ "@semantic-release/github", { From 4a6270c283157da3a3c78ca5c292694c06e7b884 Mon Sep 17 00:00:00 2001 From: HejdaJakub Date: Tue, 25 Jul 2023 15:29:48 +0200 Subject: [PATCH 10/30] chore: change commitlint rules * The body-max-line-length rule was raised from the default 100 characters to 200 characters. * Branches from the dependabot and renovate are now excluded from the commlint check. --- .commitlintrc.json | 3 ++- .github/workflows/check.yml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.commitlintrc.json b/.commitlintrc.json index eb7767f7ff..c2b9b5d78c 100644 --- a/.commitlintrc.json +++ b/.commitlintrc.json @@ -19,7 +19,8 @@ "deps", "deps-dev" ] - ] + ], + "body-max-line-length": [2, "always", 200] }, "prompt": { "questions": { diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6dff45294c..1a697d1329 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,6 +6,9 @@ on: jobs: run-commitlint-on-pr: runs-on: ubuntu-latest + if: | + startsWith(github.head_ref, 'dependabot/npm_and_yarn/') != true && + startsWith(github.head_ref, 'renovate/') != true steps: - uses: actions/checkout@v3 with: From 259e284b0e39f89a22983c3d1f2eb153107d2b24 Mon Sep 17 00:00:00 2001 From: David Flor <493294@mail.muni.cz> Date: Thu, 6 Jul 2023 13:32:41 +0200 Subject: [PATCH 11/30] feat(core): skip MFA for internal components * skip the MFA check when refreshing authz roles of service component principals (defined in perun.properties). --- .../metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 78339ab1b8..8782f3a5a1 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -362,6 +362,7 @@ public static boolean hasMFASkippableRole(PerunSession sess) throws RoleManageme List perunAdmins = new ArrayList<>(BeansUtils.getCoreConfig().getAdmins()); perunAdmins.addAll(BeansUtils.getCoreConfig().getRegistrarPrincipals()); if (perunAdmins.contains(sess.getPerunPrincipal().getActor())) { + log.debug("skipped MFA policy check for {}", sess.getPerunPrincipal().getActor()); return true; } @@ -2593,7 +2594,11 @@ public static synchronized void refreshAuthz(PerunSession sess) { } } - checkMfaForHavingRole(sess, sess.getPerunPrincipal().getRoles()); + if (!serviceRole) { + checkMfaForHavingRole(sess, sess.getPerunPrincipal().getRoles()); + } else { + log.debug("skipped MFA role check for {}", sess.getPerunPrincipal().getActor()); + } log.trace("Refreshed roles: {}", sess.getPerunPrincipal().getRoles()); sess.getPerunPrincipal().setAuthzInitialized(true); From c9bf4af5af3c4fc8752020c72beb0eb90a8d79fd Mon Sep 17 00:00:00 2001 From: HejdaJakub Date: Tue, 25 Jul 2023 14:39:33 +0200 Subject: [PATCH 12/30] fixup! feat(core): skip MFA for internal components * Remove noisy log for skipped MFA policy check. --- .../cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 8782f3a5a1..a36be71089 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -362,7 +362,6 @@ public static boolean hasMFASkippableRole(PerunSession sess) throws RoleManageme List perunAdmins = new ArrayList<>(BeansUtils.getCoreConfig().getAdmins()); perunAdmins.addAll(BeansUtils.getCoreConfig().getRegistrarPrincipals()); if (perunAdmins.contains(sess.getPerunPrincipal().getActor())) { - log.debug("skipped MFA policy check for {}", sess.getPerunPrincipal().getActor()); return true; } From 89f85572eb15461a907ee3eded0faf852b210495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Wed, 26 Jul 2023 17:34:52 +0200 Subject: [PATCH 13/30] docs(core): fixed javadoc for various methods --- .../java/cz/metacentrum/perun/core/impl/Utils.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java index e51530f784..c87efc3679 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java @@ -244,10 +244,9 @@ public static List extractAdditionalUserExtSources(PerunSessi } /** - * Returns loa of addtional ues, if not stated, returns 0. If integer cannot be parsed from input, ParserException is thrown. + * Returns loa of additional ues, if not stated, returns 0. If integer cannot be parsed from input, ParserException is thrown. * Used in extractAdditionalUserExtSources to get ues LoA. * - * @param login login of subject * @param userExtSourceRaw array containing LoA * @return int LoA */ @@ -627,11 +626,12 @@ public static User createUserFromNameMap(Map name) { /** * Creates a new instance of User with names initialized from parsed rawName. - * Imposes limit on leghts of fields. - * @see #parseCommonName(String) + * Imposes limit on lengths of fields. + * + * @see Utils#parseCommonName(String, boolean) * @param rawName raw name * @param fullNameRequired if true, throw exception if firstName or lastName is missing, do not throw exception otherwise - * @return user + * @return User */ public static User parseUserFromCommonName(String rawName, boolean fullNameRequired) { Map m = parseCommonName(rawName, fullNameRequired); @@ -639,7 +639,7 @@ public static User parseUserFromCommonName(String rawName, boolean fullNameRequi } /** - * @see Utils.parseCommonName(String rawName, boolean fullNameRequired) - where fullNameRequired is false + * @see Utils#parseCommonName(String, boolean) */ public static Map parseCommonName(String rawName) { try { From 43a15cd59fd7633c93ad4820ef051f925f81fa65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zl=C3=A1mal?= Date: Wed, 26 Jul 2023 15:39:44 +0200 Subject: [PATCH 14/30] fix(registrar): auto-approve only IdP MUNI applications in CeitecNcbr - Removed usage of LoA, we are now using IdP MUNI as marker for auto-approvable applications. --- .../perun/registrar/modules/CeitecNcbr.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java index 348ba577c0..3edb51bb4b 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java @@ -8,11 +8,11 @@ import org.slf4j.LoggerFactory; /** - * Module for CEITEC@MUNI and NCBR@MUNI VOs at CESNET instance of Perun. + * Module for NCBR@MUNI VOs at CESNET instance. * * The module - * 1. Allows (auto)approval of applications with LoA = 2. - * 2. Enforce manual approval of applications with LoA = 0, 1. + * 1. Allows (auto)approval of applications by users from IdP MUNI + * 2. Enforce manual approval of applications by users from different IdPs * * @author Pavel Zlámal */ @@ -20,11 +20,13 @@ public class CeitecNcbr extends DefaultRegistrarModule { final static Logger log = LoggerFactory.getLogger(CeitecNcbr.class); + private final static String IDP_MU = "https://idp2.ics.muni.cz/idp/shibboleth"; + @Override public void canBeApproved(PerunSession session, Application app) throws PerunException { - if (app.getExtSourceLoa() == 2) return; - throw new CantBeApprovedException("Application can't be approved automatically. LoA is: "+app.getExtSourceLoa()+". Please double check users identity before manual/force approval.", "", "", "", true, app.getId()); + if (IDP_MU.equals(app.getExtSourceName())) return; + throw new CantBeApprovedException("Application can't be approved automatically. User has not used IdP MUNI to log in. Please double check users identity before manual/force approval.", "", "", "", true, app.getId()); } From b0fed993174f2a9bae26ce21f729caa0272ac797 Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Wed, 26 Jul 2023 20:41:02 +0200 Subject: [PATCH 15/30] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20LS=5FRI=20regi?= =?UTF-8?q?strar=20modules.=20Removes=20duplicate=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract class (AbstractLifeScienceHostelRI) which the LS RI registrar modules now extend captures the common code which was present in both modules. These modules differ just in the constants they use, while they do not differ in the functionality. Common functionality has been therefore extracted into the abstract base class. --- .../modules/AbstractLifeScienceHostelRI.java | 164 ++++++++++++++++++ .../modules/LifeScienceHostelRI.java | 118 ++----------- .../modules/LifeScienceHostelRIAcc.java | 116 ++----------- 3 files changed, 186 insertions(+), 212 deletions(-) create mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java new file mode 100644 index 0000000000..0b92dbd0d6 --- /dev/null +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java @@ -0,0 +1,164 @@ +package cz.metacentrum.perun.registrar.modules; + +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.AttributesManager; +import cz.metacentrum.perun.core.api.ExtSource; +import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.UserExtSource; +import cz.metacentrum.perun.core.api.Vo; +import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.registrar.exceptions.RegistrarException; +import cz.metacentrum.perun.registrar.model.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * Abstract module for VO lifescience_hostel used in the LifeScience (LS AAI) Perun instance. + * Creates UES with LS Hostel identity and adds user to the lifescience VO directly when the application is approved. + * The concrete implementation has to specify some properties, as this module can be re-used by various instances + * (e.g. production and acceptance instances) + * + * @see cz.metacentrum.perun.registrar.modules.LifeScienceHostelRI for production instance module + * @see cz.metacentrum.perun.registrar.modules.LifeScienceHostelRIAcc for acceptance instance module + * + * @author Pavel Vyskocil + * @author Dominik Frantisek Bucik + */ +public abstract class AbstractLifeScienceHostelRI extends DefaultRegistrarModule { + + private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); + + private final static String VO_SHORTNAME = "lifescience"; + + private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; + + private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; + + + /** + * Create proper UserExtSource + */ + @Override + public Application approveApplication(PerunSession session, Application app) + throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, + WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, + ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, + VoNotExistsException, ExtendMembershipException, AlreadyMemberException + { + PerunBl perun = (PerunBl)session.getPerun(); + + User user = app.getUser(); + if (user != null) { + // Create UES for user + Attribute userLogin = perun.getAttributesManagerBl().getAttribute( + session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); + if (userLogin != null && userLogin.getValue() != null) { + UserExtSource ues = storeAndGetUserExtSource(session, user, userLogin, perun); + setAuidIntoUesAttributes(session, ues, perun); + } + + if (Application.AppType.INITIAL.equals(app.getType())) { + try { + Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); + Member member = perun.getMembersManagerBl().createMember(session, vo, user); + perun.getMembersManagerBl().validateMemberAsync(session, member); + log.debug("LS Hostel member added to the main VO Lifescience {}", member); + } catch (VoNotExistsException e) { + log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); + } catch (AlreadyMemberException ignore) { + // user is already in lifescience + } catch (ExtendMembershipException e) { + // can't be member of lifescience, shouldn't happen + log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); + } + } + // User doesn't have login - don't set UES + } + + return app; + } + + /** + * Get scope part of the user login (including @ character) + * @return scope + */ + protected abstract String getScope(); + + /** + * Get name of the ExtSource for which the login is generated + * @return ExtSource name + */ + protected abstract String getExtSourceName(); + + /** + * Creates the UserExtSource object with user login and returns it. + * If the UES already exists, method just returns it. + */ + private UserExtSource storeAndGetUserExtSource(PerunSession session, User user, Attribute userLogin, PerunBl perun) + throws ExtSourceNotExistsException + { + ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, getExtSourceName()); + String login = userLogin.valueAsString(); + UserExtSource ues = new UserExtSource(extSource, login + getScope()); + ues.setLoa(0); + + try { + ues = perun.getUsersManagerBl().addUserExtSource(session, user, ues); + } catch (UserExtSourceExistsException ex) { + try { + ues = perun.getUsersManagerBl().getUserExtSourceByExtLogin(session, extSource, login); + } catch (UserExtSourceNotExistsException e) { + // should not happen due to parent catch block + } + } + return ues; + } + + /** + * Stores the user login from passed UserExtSource into the AUIDS attribute (constant AUIDS_ATTRIBUTE). + */ + private void setAuidIntoUesAttributes(PerunSession session, UserExtSource ues, PerunBl perun) + throws WrongAttributeAssignmentException, WrongReferenceAttributeValueException, PrivilegeException, + WrongAttributeValueException + { + try { + Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); + Set attrValue = new HashSet<>(); + if (auidsAttr.getValue() != null + && auidsAttr.valueAsList() != null + && !auidsAttr.valueAsList().isEmpty() + ) { + attrValue.addAll(auidsAttr.valueAsList()); + } + attrValue.add(ues.getLogin()); + auidsAttr.setValue(new ArrayList<>(attrValue)); + perun.getAttributesManager().setAttribute(session, ues, auidsAttr); + } catch (UserExtSourceNotExistsException e) { + // should not happen + } catch (AttributeNotExistsException e) { + // ok, attribute is probably not used + } + } + +} + diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java index fd944d87b9..404a3e7948 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java @@ -1,123 +1,27 @@ package cz.metacentrum.perun.registrar.modules; -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.RegistrarException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - /** * Module for VO lifescience_hostel on LifeScience Perun machine - * * On approval create UES with LS Hostel identity and add user to the lifescience VO directly. * * @author Pavel Vyskocil */ -public class LifeScienceHostelRI extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); - - private final static String HOSTEL_HOSTANAME = "hostel.aai.lifescience-ri.eu"; - private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; - private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; - private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; - private final static String VO_SHORTNAME = "lifescience"; - private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; - - /** - * Create proper UserExtSource - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, VoNotExistsException, ExtendMembershipException, AlreadyMemberException { - - PerunBl perun = (PerunBl)session.getPerun(); - - User user = app.getUser(); +public class LifeScienceHostelRI extends AbstractLifeScienceHostelRI { - if (user != null) { + private final static String HOSTEL_HOSTNAME = "hostel.aai.lifescience-ri.eu"; - // Create UES for user - Attribute userLogin = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); - if (userLogin != null && userLogin.getValue() != null) { - ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, LS_HOSTEL_EXT_SOURCE_NAME); - String login = userLogin.valueAsString(); - UserExtSource ues = new UserExtSource(extSource, login + LS_HOSTEL_SCOPE); - ues.setLoa(0); + private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTNAME; - try { - ues = perun.getUsersManagerBl().addUserExtSource(session, user, ues); - } catch (UserExtSourceExistsException ex) { - // this is OK - } - - try { - Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); - List attrValue = new ArrayList<>(); - if (auidsAttr.getValue() != null - && auidsAttr.valueAsList() != null - && !auidsAttr.valueAsList().isEmpty() - ) { - attrValue = auidsAttr.valueAsList(); - } - auidsAttr.valueAsList().add(login + LS_HOSTEL_SCOPE); - auidsAttr.setValue(attrValue); - perun.getAttributesManager().setAttribute(session, ues, auidsAttr); - } catch (UserExtSourceNotExistsException e) { - // should not happen - } catch (AttributeNotExistsException e) { - // ok, attribute is probably not used - } - - } - - if (Application.AppType.INITIAL.equals(app.getType())) { - try { - Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); - Member member = perun.getMembersManagerBl().createMember(session, vo, user); - perun.getMembersManagerBl().validateMemberAsync(session, member); - log.debug("LS Hostel member added to the main VO Lifescience {}", member); - } catch (VoNotExistsException e) { - log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); - } catch (AlreadyMemberException ignore) { - // user is already in lifescience - } catch (ExtendMembershipException e) { - // can't be member of lifescience, shouldn't happen - log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); - } - } - - // User doesn't have login - don't set UES - - } - - return app; + private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTNAME + "/lshostel/"; + @Override + protected String getScope() { + return LS_HOSTEL_SCOPE; } + @Override + protected String getExtSourceName() { + return LS_HOSTEL_EXT_SOURCE_NAME; + } } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java index 1b0b6ba439..5dc93d27e8 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java @@ -1,36 +1,5 @@ package cz.metacentrum.perun.registrar.modules; -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.RegistrarException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - /** * Module for VO lifescience_hostel on LifeScience acceptance Perun machine * @@ -38,85 +7,22 @@ * * @author Pavel Vyskocil */ -public class LifeScienceHostelRIAcc extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); - - private final static String HOSTEL_HOSTANAME = "hostel.acc.aai.lifescience-ri.eu"; - private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; - private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; - private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; - private final static String VO_SHORTNAME = "lifescience"; - private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; - - /** - * Create proper UserExtSource - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, VoNotExistsException, ExtendMembershipException, AlreadyMemberException { - - PerunBl perun = (PerunBl)session.getPerun(); - - User user = app.getUser(); +public class LifeScienceHostelRIAcc extends AbstractLifeScienceHostelRI { - if (user != null) { + private final static String HOSTEL_HOSTNAME = "hostel.acc.aai.lifescience-ri.eu"; - // Create UES for user - Attribute userLogin = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); - if (userLogin != null && userLogin.getValue() != null) { - ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, LS_HOSTEL_EXT_SOURCE_NAME); - String login = userLogin.valueAsString(); - UserExtSource ues = new UserExtSource(extSource, login + LS_HOSTEL_SCOPE); - ues.setLoa(0); + private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTNAME; - try { - perun.getUsersManagerBl().addUserExtSource(session, user, ues); - } catch (UserExtSourceExistsException ex) { - // this is OK - } + private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTNAME + "/lshostel/"; - try { - Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); - List attrValue = new ArrayList<>(); - if (auidsAttr.getValue() != null - && auidsAttr.valueAsList() != null - && !auidsAttr.valueAsList().isEmpty() - ) { - attrValue = auidsAttr.valueAsList(); - } - auidsAttr.valueAsList().add(login + LS_HOSTEL_SCOPE); - auidsAttr.setValue(attrValue); - perun.getAttributesManager().setAttribute(session, ues, auidsAttr); - } catch (UserExtSourceNotExistsException e) { - // should not happen - } catch (AttributeNotExistsException e) { - // ok, attribute is probably not used - } - - } - - if (Application.AppType.INITIAL.equals(app.getType())) { - try { - Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); - Member member = perun.getMembersManagerBl().createMember(session, vo, user); - perun.getMembersManagerBl().validateMemberAsync(session, member); - log.debug("LS Hostel member added to the main VO Lifescience {}", member); - } catch (VoNotExistsException e) { - log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); - } catch (AlreadyMemberException ignore) { - // user is already in lifescience - } catch (ExtendMembershipException e) { - // can't be member of lifescience, shouldn't happen - log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); - } - } - - // User doesn't have login - don't set UES - - } - - return app; + @Override + protected String getScope() { + return LS_HOSTEL_SCOPE; + } + @Override + protected String getExtSourceName() { + return LS_HOSTEL_EXT_SOURCE_NAME; } } From 6c9ee15e977bad0f7de4ea62ad5afc3730d214ce Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Tue, 1 Aug 2023 13:47:48 +0200 Subject: [PATCH 16/30] fix(core): getFacilityById fix policy Allow resource-related roles access to the facility. --- perun-base/src/main/resources/perun-roles.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 49812e3cb3..7a3d343512 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -638,6 +638,9 @@ perun_policies: - ENGINE: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility + - RESOURCESELFSERVICE: Facility + - RESOURCEADMIN: Facility + - RESOURCEOBSERVER: Facility - PERUNOBSERVER: - SPREGAPPLICATION: include_policies: From 9274d3cb2edb65e1f8e8479b2ea899266bc7a055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ou=C5=A1ek?= Date: Tue, 1 Aug 2023 19:17:49 +0200 Subject: [PATCH 17/30] feat: enable facility search for SP reg role --- perun-base/src/main/resources/perun-roles.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 7a3d343512..62b2cd3a28 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -729,6 +729,7 @@ perun_policies: - FACILITYADMIN: - FACILITYOBSERVER: - PERUNOBSERVER: + - SPREGAPPLICATION: - PROXY: include_policies: - default_policy @@ -738,6 +739,7 @@ perun_policies: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility - PERUNOBSERVER: + - SPREGAPPLICATION: - PROXY: include_policies: - default_policy From c3654b63092e99c212823d60e40f67dbf8e15871 Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Tue, 11 Jul 2023 21:27:17 +0200 Subject: [PATCH 18/30] feat(core): remove not allowed roles Depending on where the request comes from, remove roles that are not allowed. DEPLOYMENT NOTE: New property 'appAllowedRoles' added to the CoreConfig. In perun.properties define 'perun.appAllowedRoles.apps' as a list of names of apps where role limitation is necessary. For each app name, define regex which maps to the Referer header of the request coming from the given app and a list of allowed roles. For example: perun.appAllowedRoles.apps=registrar perun.appAllowedRoles.registrar.reg=^.*/registrar/.*$ perun.appAllowedRoles.registrar.roles=SELF,MFA --- .../perun/core/api/CoreConfig.java | 30 +++++++++++++++++++ .../perun/core/api/PerunPrincipal.java | 16 ++++++++++ perun-base/src/main/resources/perun-base.xml | 3 ++ .../core/blImpl/AuthzResolverBlImpl.java | 14 +++++++++ .../java/cz/metacentrum/perun/rpc/Api.java | 6 ++-- 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java index f12d3e198f..eeebb5c51f 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java @@ -99,6 +99,7 @@ public void initBeansUtils() { private int mfaAuthTimeout; private int mfaAuthTimeoutPercentageForceLogIn; private boolean enforceMfa; + private Map> appAllowedRoles = new HashMap<>(); private int idpLoginValidity; private List idpLoginValidityExceptions; private int roleUpdateInterval; @@ -451,6 +452,35 @@ private String getOidcIssuerProperty(String issuer, String suffix) { return value; } + public Map> getAppAllowedRoles() { + return appAllowedRoles; + } + + public void setAppAllowedRoles(List apps) { + for (String app : apps) { + String regex = getAppAllowedRolesProperty(app, "reg"); + if (regex == null) continue; + + String rolesProperty = getAppAllowedRolesProperty(app, "roles"); + if (rolesProperty == null) continue; + + List roles = List.of(rolesProperty.split("\s*,\s*")); + + log.debug("registering application {} by regex={} with roles={}", app, regex, roles); + + this.appAllowedRoles.put(regex, roles); + } + } + + private String getAppAllowedRolesProperty(String app, String suffix) { + String property = "perun.appAllowedRoles." + app + "." + suffix; + String value = properties.getProperty(property); + if (value == null) { + log.error("property {} not found, skipping allowed roles for application {}", property, app); + } + return value; + } + public Map getOidcIssuersExtsourceNames() { return oidcIssuersExtsourceNames; } diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java index 50516b3550..070c30c7c2 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java @@ -20,6 +20,8 @@ public class PerunPrincipal { private User user; // Contains principal's roles together with objects which specifies the role, e.g. VOADMIN -> list contains VO names private volatile AuthzRoles authzRoles = new AuthzRoles(); + // The PERUNADMIN role is enabled and will not be manually removed + private String referer = ""; // Time of the last update of roles private long rolesUpdatedAt = System.currentTimeMillis(); // Map contains additional attributes, e.g. from authentication system @@ -62,6 +64,12 @@ public PerunPrincipal(String actor, String extSourceName, String extSourceType, this.additionalInformations = additionalInformations; } + public PerunPrincipal(String actor, String extSourceName, String extSourceType, int extSourceLoa, Map additionalInformations, String referer) { + this(actor, extSourceName, extSourceType, extSourceLoa); + this.additionalInformations = additionalInformations; + this.referer = referer; + } + /** * Returns actor string representation. * @return string representing actor @@ -153,6 +161,14 @@ public void setRolesUpdatedAt(long rolesUpdatedAt) { this.rolesUpdatedAt = rolesUpdatedAt; } + public String getReferer() { + return referer; + } + + public void setReferer(String referer) { + this.referer = referer; + } + @Override public int hashCode() { final int prime = 31; diff --git a/perun-base/src/main/resources/perun-base.xml b/perun-base/src/main/resources/perun-base.xml index c8f9be10d3..f17a957a55 100644 --- a/perun-base/src/main/resources/perun-base.xml +++ b/perun-base/src/main/resources/perun-base.xml @@ -82,6 +82,7 @@ + @@ -169,6 +170,8 @@ https://login.elixir-czech.org/idp/ cz.metacentrum.perun.core.impl.ExtSourceIdp + + true localhost diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index a36be71089..c0fde75c78 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -99,6 +99,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static cz.metacentrum.perun.core.api.AuthzResolver.MFA_CRITICAL_ATTR; @@ -2572,6 +2573,19 @@ public static synchronized void refreshAuthz(PerunSession sess) { setAdditionalRoles(sess, roles, user); } + // Remove roles which are not allowed + Map> appAllowedRoles = BeansUtils.getCoreConfig().getAppAllowedRoles(); + for (String reg : appAllowedRoles.keySet()) { + Pattern pattern = Pattern.compile(reg); + if (pattern.matcher(sess.getPerunPrincipal().getReferer()).matches()) { + for (String role : roles.getRolesNames()) { + if (!appAllowedRoles.get(reg).contains(role)) { + roles.remove(role); + } + } + } + } + sess.getPerunPrincipal().setRoles(roles); if (sess.getPerunClient().getType() == PerunClient.Type.OAUTH) { diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java index 747e27ef26..c9ae71d6b2 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java @@ -225,6 +225,8 @@ private static PerunPrincipal setupPerunPrincipal(HttpServletRequest req, Deseri int extSourceLoa; Map additionalInformations = new HashMap<>(); + String referer = req.getHeader("Referer"); + String shibIdentityProvider = getStringAttribute(req, SHIB_IDENTITY_PROVIDER); String sourceIdpEntityId = getStringAttribute(req, SOURCE_IDP_ENTITY_ID); String remoteUser = req.getRemoteUser(); @@ -415,8 +417,8 @@ else if (Objects.equals(req.getAttribute(SSL_CLIENT_VERIFY), SUCCESS)) { if (isEmpty(extLogin) || isEmpty(extSourceName)) { throw new UserNotExistsException("extLogin or extSourceName is empty"); } - log.trace("creating PerunPrincipal(actor={},extSourceName={},extSourceType={},extSourceLoa={},additionalInformations={})",extLogin,extSourceName, extSourceType, extSourceLoa, additionalInformations); - return new PerunPrincipal(extLogin, extSourceName, extSourceType, extSourceLoa, additionalInformations); + log.trace("creating PerunPrincipal(actor={},extSourceName={},extSourceType={},extSourceLoa={},additionalInformations={},referer={})",extLogin,extSourceName, extSourceType, extSourceLoa, additionalInformations, referer); + return new PerunPrincipal(extLogin, extSourceName, extSourceType, extSourceLoa, additionalInformations, referer); } private PerunClient setupPerunClient(HttpServletRequest req) { From 1f1cdc338fc0d73becb3c975c5571fa01a8fa34c Mon Sep 17 00:00:00 2001 From: sarkapalkovicova Date: Tue, 1 Aug 2023 09:27:12 +0200 Subject: [PATCH 19/30] fix(core): service account skip MFA Skip MFA when user is service user. --- .../cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index a36be71089..bdbb976e99 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -2593,10 +2593,10 @@ public static synchronized void refreshAuthz(PerunSession sess) { } } - if (!serviceRole) { + if (!serviceRole && (sess.getPerunPrincipal().getUser() == null || !sess.getPerunPrincipal().getUser().isServiceUser())) { checkMfaForHavingRole(sess, sess.getPerunPrincipal().getRoles()); } else { - log.debug("skipped MFA role check for {}", sess.getPerunPrincipal().getActor()); + log.debug("skipped MFA role check for {}", serviceRole ? sess.getPerunPrincipal().getActor() : sess.getPerunPrincipal().getUser()); } log.trace("Refreshed roles: {}", sess.getPerunPrincipal().getRoles()); From 6e2920261be0cea64bc84c42610842824478c2dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 20:04:13 +0000 Subject: [PATCH 20/30] chore(deps): update dependency inquirer to v8.2.6 --- package-lock.json | 275 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 267 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ae544af59..68b93fa55b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2234,6 +2234,85 @@ "node": ">= 12" } }, + "node_modules/commitizen/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/commitizen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/commitizen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/commitizen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/commitizen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commitizen/node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/commitizen/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -2249,6 +2328,41 @@ "node": ">=10" } }, + "node_modules/commitizen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commitizen/node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/commitizen/node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -2270,6 +2384,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/commitizen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/commitizen/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -3432,9 +3570,9 @@ "dev": true }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -3451,7 +3589,7 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "engines": { "node": ">=12.0.0" @@ -3569,6 +3707,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/into-stream": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", @@ -11137,6 +11289,58 @@ "strip-json-comments": "3.1.1" }, "dependencies": { + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -11149,6 +11353,35 @@ "universalify": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -11160,6 +11393,21 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true } } }, @@ -12022,9 +12270,9 @@ "dev": true }, "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -12041,7 +12289,7 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "dependencies": { "ansi-escapes": { @@ -12116,6 +12364,17 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } } } }, From 8cee9f607bb73631b565c472b956a678f1964619 Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Fri, 12 May 2023 06:05:28 +0200 Subject: [PATCH 21/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20BBMRIResources=20r?= =?UTF-8?q?eg.=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce generalized module for registrar replacing BBMRI Collections and BBMRI Networks. Previously used modules are marked as deprecated. --- .../registrar/modules/BBMRICollections.java | 1 + .../registrar/modules/BBMRINetworks.java | 1 + .../registrar/modules/BBMRIResources.java | 314 ++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java index cd196a682f..e785354100 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java @@ -40,6 +40,7 @@ * @author Jiri Mauritz (original) * @author Dominik Frantisek Bucik (modifications) */ +@Deprecated public class BBMRICollections extends DefaultRegistrarModule { private final static Logger log = LoggerFactory.getLogger(BBMRICollections.class); diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java index 09936aa99c..f32a182444 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java @@ -44,6 +44,7 @@ * @author Jiri Mauritz (original) * @author Dominik Frantisek Bucik (modifications) */ +@Deprecated public class BBMRINetworks extends DefaultRegistrarModule { private final static Logger log = LoggerFactory.getLogger(BBMRINetworks.class); diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java new file mode 100644 index 0000000000..72322d3e1c --- /dev/null +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java @@ -0,0 +1,314 @@ +package cz.metacentrum.perun.registrar.modules; + +import com.google.common.base.Strings; +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.Group; +import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.Vo; +import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; +import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotGroupMemberException; +import cz.metacentrum.perun.core.api.exceptions.PerunException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; +import cz.metacentrum.perun.registrar.exceptions.RegistrarException; +import cz.metacentrum.perun.registrar.model.Application; +import cz.metacentrum.perun.registrar.model.ApplicationFormItemData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Registration module for BBMRI Collections, Networks, canSERV services, etc. + * Module: + * 1. reads input with IDs of the resources and checks, whether groups representing resources exist + * - uses the content of attribute RESOURCE_ID_ATTR_NAME as the name of the attribute containing IDs of the + * resources on groups + * - parses the root group, under which the groups representing specified resources are located + * - if RESOURCE_ORIGIN_ENABLED_ATTR_NAME is set to true, the root group will be parsed from input from the form + * (where input is select named "resourceOrigin") + * - else if RESOURCES_ROOT_GROUP_ATTR_NAME is set, the root group will be parsed from the value of this attribute + * - else the root group will be considered the target group of the application + * 2. adds users to the appropriate groups + * + * @author Dominik Frantisek Bucik + */ +public class BBMRIResources extends DefaultRegistrarModule { + + private final static Logger log = LoggerFactory.getLogger(BBMRIResources.class); + + // field names + private static final String RESOURCE_IDS = "resourceIds"; + private static final String RESOURCE_ORIGIN = "resourceOrigin"; + + // configuration attributes + private static final String RESOURCE_ID_ATTR_NAME = "urn:perun:group:attribute-def:def:resourceIDAttrName"; + private static final String RESOURCE_ORIGIN_ENABLED_ATTR_NAME = "urn:perun:group:attribute-def:def:resourceOriginEnabled"; + private static final String RESOURCES_ROOT_GROUP_ATTR_NAME = "urn:perun:group:attribute-def:def:resourcesRootGroup"; + + /** + * Finds groups representing resources by provided input, adds user into these groups and removes from the + * group where this application form is used. + * + * @param session who approves the application + * @param app application + * @return unchanged application + * @throws PerunException in case of internal error in Perun + */ + @Override + public Application approveApplication(PerunSession session, Application app) + throws VoNotExistsException, UserNotExistsException, PrivilegeException, + MemberNotExistsException, RegistrarException, GroupNotExistsException, + AttributeNotExistsException, WrongAttributeAssignmentException, ExternallyManagedException, + WrongAttributeValueException, WrongReferenceAttributeValueException, NotGroupMemberException + { + if (app.getGroup() == null) { + throw new RegistrarException( + "Invalid usage of registrar module - module '" + this.getClass().getName() + + "' should be used on group level only"); + } + + // get perun and beans from session + PerunBl perun = (PerunBl) session.getPerun(); + Vo vo = app.getVo(); + User user = app.getUser(); + Member member; + try { + member = perun.getMembersManagerBl().getMemberByUser(session, vo, user); + } catch (MemberNotExistsException ex) { + log.error("User {} is not member in the VO {}", user, vo); + throw new RegistrarException("Cannot approve application - user is not member of the VO"); + } + + // get IDs of resources specified by the user + Set resourceIDsInApplication = getResourceIDsFromApplication(session, app); + // get IDs of resources represented as groups in the system + Map resourceIDToGroupMapsInSystem = getPerunResourceIdToGroupMap(session, app, perun); + + // add user to all groups from the field on application + for (String resourceId : resourceIDsInApplication) { + Group resource = resourceIDToGroupMapsInSystem.getOrDefault(resourceId, null); + if (resource == null) { + log.debug("There is no group for resource with ID: '{}'", resourceId); + } else { + try { + perun.getGroupsManagerBl().addMember(session, resource, member); + } catch (AlreadyMemberException ex) { + // ignore + } + } + } + try { + perun.getGroupsManagerBl().removeMember(session, app.getGroup(), member); + } catch (NotGroupMemberException e) { + //we can ignore this exception + } + + return app; + } + + /** + * Checks whether all resource IDs found in user input really exists in Perun. + * If not, CantBeApproved exception is thrown. + * + * @param session who approves the application + * @param app unchanged application + * @throws CantBeApprovedException if at least one resource ID does not exist in Perun + */ + @Override + public void canBeApproved(PerunSession session, Application app) throws PerunException { + // get perun and beans from session + PerunBl perun = (PerunBl) session.getPerun(); + + // get IDs of resources specified by the user + Set resourceIDsInApplication = getResourceIDsFromApplication(session, app); + // get IDs of resources represented as groups in the system + Set resourceIDsInSystem = getPerunResourceIdToGroupMap(session, app, perun).keySet(); + // remove existing resources, so we get invalid inputs + resourceIDsInApplication.removeAll(resourceIDsInSystem); + + // difference must be empty + if (!resourceIDsInApplication.isEmpty()) { + throw new CantBeApprovedException("Resources " + resourceIDsInApplication + " do not exist." + + "If you approve the application, these resources will be skipped.", "", "", "", true, app.getId()); + } + } + + /** + * Gets Resources present in the system as a map of the Resource ID to the group representing particular resource. + * + * @return Map of String to Group, where key is the ID of the resource and Group is the representation + */ + private Map getPerunResourceIdToGroupMap(PerunSession session, Application app, PerunBl perun) + throws PrivilegeException, RegistrarException, VoNotExistsException, WrongAttributeAssignmentException, + AttributeNotExistsException, GroupNotExistsException + { + // get root group for resources hierarchy + Group resourceOriginGroup = getResourceOriginGroup(session, app, perun); + // get name of the attribute (stored in RESOURCE_ID_ATTR_NAME attribute) containing ID of the resource + String resourceIdAttributeName = getResourceIdAttributeName(session, app, perun); + + // get map of ResourceID -> group (representing resource) + return getResourceIDsToGroupsMap(session, perun, resourceOriginGroup, resourceIdAttributeName); + } + + /** + * Gets name of the attribute containing Resource IDs. This attribute should be used at the groups + * identifying resources. + * + * @return name of the attribute where ID of the resource is stored, null if value is not set + */ + private String getResourceIdAttributeName(PerunSession session, Application app, PerunBl perun) + throws WrongAttributeAssignmentException, AttributeNotExistsException + { + return perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCE_ID_ATTR_NAME) + .valueAsString(); + } + + /** + * Gets root group, under which subgroups representing resources are placed. + * + * @return resource IDs set + */ + private Group getResourceOriginGroup(PerunSession session, Application app, PerunBl perun) + throws PrivilegeException, RegistrarException, VoNotExistsException + { + try { + if (perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCE_ORIGIN_ENABLED_ATTR_NAME) + .valueAsBoolean() + ) { + try { + String resourceOriginGroupName = getResourceOriginGroupNameFromApplication(session, app); + return perun.getGroupsManagerBl().getGroupByName(session, app.getVo(), resourceOriginGroupName); + } catch (GroupNotExistsException e) { + throw new InternalErrorException("Target group does not exist"); + } + } + } catch (AttributeNotExistsException | WrongAttributeAssignmentException ex) { + //OK, we consider it as disabled, try manually configured resource root group + } + + try { + try { + String resourceOriginGroupName = perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCES_ROOT_GROUP_ATTR_NAME) + .valueAsString(); + return perun.getGroupsManagerBl().getGroupByName(session, app.getVo(), resourceOriginGroupName); + } catch (GroupNotExistsException e) { + throw new InternalErrorException("Target group does not exist"); + } + } catch (AttributeNotExistsException | WrongAttributeAssignmentException exc) { + // OK, root will be the app group + } + + return app.getGroup(); + } + + /** + * Gets name of target group, where subgroups representing resources are placed. + * + * @return resource IDs set + */ + private String getResourceOriginGroupNameFromApplication(PerunSession session, Application app) + throws RegistrarException, PrivilegeException + { + String resourceOriginGroupName = null; + List formData = registrar.getApplicationDataById(session, app.getId()); + for (ApplicationFormItemData field : formData) { + if (RESOURCE_ORIGIN.equals(field.getShortname())) { + resourceOriginGroupName = field.getValue(); + break; + } + } + + if (resourceOriginGroupName == null) { + throw new InternalErrorException("There is no field with target group name on the registration form."); + } + + return resourceOriginGroupName; + } + + /** + * Gets resource IDs from a field on the application form with short name. + * + * @return resource IDs set + */ + private Set getResourceIDsFromApplication(PerunSession session, Application app) + throws RegistrarException, PrivilegeException + { + String resourceIdsString = null; + List formData = registrar.getApplicationDataById(session, app.getId()); + for (ApplicationFormItemData field : formData) { + if (RESOURCE_IDS.equals(field.getShortname())) { + resourceIdsString = field.getValue(); + break; + } + } + + if (resourceIdsString == null) { + throw new InternalErrorException("There is no field with resource IDs on the registration form."); + } + + // get set of resource IDs from application + Set resourceIDsInApplication = new HashSet<>(); + for (String resource : resourceIdsString.split("[,\n ]+")) { + resourceIDsInApplication.add(resource.trim()); + } + + return resourceIDsInApplication; + } + + /** + * Gets resources as map of resourceID => Group. + * + * @return Map of resource IDs to group. + */ + private Map getResourceIDsToGroupsMap(PerunSession session, + PerunBl perun, + Group resourceOriginGroup, + String resourceIdContainingAttribute) + throws WrongAttributeAssignmentException, AttributeNotExistsException + { + Map resourceIDsToGroupMap = new HashMap<>(); + + List resourceGroups = perun.getGroupsManagerBl().getSubGroups(session, resourceOriginGroup); + if (resourceGroups == null || resourceGroups.isEmpty()) { + log.debug("No resource groups found, returning empty map."); + return resourceIDsToGroupMap; + } + + for (Group resourceGroup : resourceGroups) { + Attribute resourceIDAttr = perun.getAttributesManagerBl() + .getAttribute(session, resourceGroup, resourceIdContainingAttribute); + + if (resourceIDAttr == null || Strings.isNullOrEmpty(resourceIDAttr.valueAsString())) { + log.warn("Found resource group ({}) without value in attr {}: ({})", + resourceGroup, resourceIdContainingAttribute, resourceIDAttr); + } else { + resourceIDsToGroupMap.put(resourceIDAttr.valueAsString(), resourceGroup); + } + } + + return resourceIDsToGroupMap; + } + +} From 1608da50bd1d62842e5b6d18475bee9f273a63b2 Mon Sep 17 00:00:00 2001 From: Rastislav Krutak <492918@mail.muni.cz> Date: Mon, 31 Jul 2023 13:08:39 +0200 Subject: [PATCH 22/30] feat(core): extend authz table with audit attributes Added created_at, created_by, and created_by_uid columns to authz table and modified the inserts to include them. The setRole method modified to use prepared statement since user supplied input will be in the statement. BREAKING CHANGE: Added created_at and created_by columns to authz table. --- perun-base/src/test/resources/test-schema.sql | 6 +- .../perun/core/impl/AuthzResolverImpl.java | 94 +++++++++++-------- .../src/main/resources/postgresChangelog.txt | 4 + .../api/AuthzResolverIntegrationTest.java | 4 + perun-db/postgres.sql | 6 +- 5 files changed, 70 insertions(+), 44 deletions(-) diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 11b55ea733..708ed6ffff 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.15 (don't forget to update insert statement at the end of file) +-- database version 3.2.16 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -1600,6 +1600,8 @@ create table authz ( modified_by_uid integer, authorized_group_id integer, --identifier of whole authorized group security_team_id integer, --identifier of security team + created_at timestamp default statement_timestamp() not null, + created_by varchar default user not null, constraint authz_role_fk foreign key (role_id) references roles(id), constraint authz_user_fk foreign key (user_id) references users(id), constraint authz_authz_group_fk foreign key (authorized_group_id) references groups(id), @@ -1905,7 +1907,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.15'); +insert into configurations values ('DATABASE VERSION','3.2.16'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); insert into membership_types (id, membership_type, description) values (2, 'INDIRECT', 'Member is added indirectly through UNION relation'); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java index fe9ecca4a9..5ab9b338eb 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java @@ -38,6 +38,7 @@ import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import javax.sql.DataSource; import java.util.ArrayList; @@ -449,7 +450,8 @@ public void removeAllAuthzForSecurityTeam(PerunSession sess, SecurityTeam securi @Override public void addAdmin(PerunSession sess, Facility facility, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, facility_id) values (?, (select id from roles where name=?), ?)", user.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId()); + jdbc.update("insert into authz (user_id, role_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin of the facility " + facility, e, user, facility); } catch (RuntimeException e) { @@ -460,7 +462,8 @@ public void addAdmin(PerunSession sess, Facility facility, User user) throws Alr @Override public void addAdmin(PerunSession sess, Facility facility, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, facility_id) values (?, (select id from roles where name=?), ?)", group.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin of the facility " + facility, e, group, facility); } catch (RuntimeException e) { @@ -493,7 +496,8 @@ public void removeAdmin(PerunSession sess, Facility facility, Group group) throw @Override public void addAdmin(PerunSession sess, Resource resource, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", user.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin of the resource " + resource, e, user, resource); } catch (RuntimeException e) { @@ -504,7 +508,8 @@ public void addAdmin(PerunSession sess, Resource resource, User user) throws Alr @Override public void addAdmin(PerunSession sess, Resource resource, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (authorized_group_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", group.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin of the resource " + resource, e, group, resource); } catch (RuntimeException e) { @@ -537,7 +542,8 @@ public void removeAdmin(PerunSession sess, Resource resource, Group group) throw @Override public void addAdmin(PerunSession sess, User sponsoredUser, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, sponsored_user_id) values (?, (select id from roles where name=?), ?)", user.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId()); + jdbc.update("insert into authz (user_id, role_id, sponsored_user_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already sponsor of the sponsoredUser " + sponsoredUser, e, user, sponsoredUser); } catch (RuntimeException e) { @@ -548,7 +554,8 @@ public void addAdmin(PerunSession sess, User sponsoredUser, User user) throws Al @Override public void addAdmin(PerunSession sess, User sponsoredUser, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, sponsored_user_id) values (?, (select id from roles where name=?), ?)", group.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, sponsored_user_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already sponsor of the sponsoredUser " + sponsoredUser, e, group, sponsoredUser); } catch (RuntimeException e) { @@ -582,8 +589,9 @@ public void removeAdmin(PerunSession sess, User sponsoredUser, Group group) thro public void addAdmin(PerunSession sess, Group group, User user) throws AlreadyAdminException { try { // Add GROUPADMIN role + groupId and voId - jdbc.update("insert into authz (user_id, role_id, group_id, vo_id) values (?, (select id from roles where name=?), ?, ?)", - user.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId()); + jdbc.update("insert into authz (user_id, role_id, group_id, vo_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?)", + user.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin in group " + group, e, user, group); } catch (RuntimeException e) { @@ -594,8 +602,9 @@ public void addAdmin(PerunSession sess, Group group, User user) throws AlreadyAd @Override public void addAdmin(PerunSession sess, Group group, Group authorizedGroup) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, group_id, vo_id) values (?, (select id from roles where name=?), ?, ?)", - authorizedGroup.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId()); + jdbc.update("insert into authz (authorized_group_id, role_id, group_id, vo_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?)", + authorizedGroup.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + authorizedGroup.getId() + " is already group admin in group " + group, e, authorizedGroup, group); } catch (RuntimeException e) { @@ -630,8 +639,9 @@ public void removeAdmin(PerunSession sess, Group group, Group authorizedGroup) t @Override public void addAdmin(PerunSession sess, SecurityTeam securityTeam, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, security_team_id) values (?, (select id from roles where name=?), ?)", user.getId(), - Role.SECURITYADMIN.toLowerCase(), securityTeam.getId()); + jdbc.update("insert into authz (user_id, role_id, security_team_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), + Role.SECURITYADMIN.toLowerCase(), securityTeam.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin in securityTeam " + securityTeam, e, user, securityTeam); } catch (RuntimeException e) { @@ -642,8 +652,9 @@ public void addAdmin(PerunSession sess, SecurityTeam securityTeam, User user) th @Override public void addAdmin(PerunSession sess, SecurityTeam securityTeam, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, security_team_id) values (?, (select id from roles where name=?), ?)", group.getId(), - Role.SECURITYADMIN.toLowerCase(), securityTeam.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, security_team_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), + Role.SECURITYADMIN.toLowerCase(), securityTeam.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin in securityTeam " + securityTeam, e, group, securityTeam); } catch (RuntimeException e) { @@ -676,7 +687,8 @@ public void removeAdmin(PerunSession sess, SecurityTeam securityTeam, Group grou @Override public void makeUserPerunAdmin(PerunSession sess, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.PERUNADMIN.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.PERUNADMIN.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already perun admin", e, user, Role.PERUNADMIN); } catch (RuntimeException e) { @@ -687,7 +699,8 @@ public void makeUserPerunAdmin(PerunSession sess, User user) throws AlreadyAdmin @Override public void makeUserPerunObserver(PerunSession sess, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.PERUNOBSERVER.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.PERUNOBSERVER.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already perun observer", e, user, Role.PERUNOBSERVER); } catch (RuntimeException e) { @@ -698,7 +711,8 @@ public void makeUserPerunObserver(PerunSession sess, User user) throws AlreadyAd @Override public void makeAuthorizedGroupPerunObserver(PerunSession sess, Group authorizedGroup) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id) values (?, (select id from roles where name=?))", authorizedGroup.getId(), Role.PERUNOBSERVER.toLowerCase()); + jdbc.update("insert into authz (authorized_group_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", authorizedGroup.getId(), Role.PERUNOBSERVER.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + authorizedGroup.getId() + " is already perun observer", e, authorizedGroup, Role.PERUNOBSERVER); } catch (RuntimeException e) { @@ -742,7 +756,8 @@ public void removePerunObserver(PerunSession sess, User user) throws UserNotAdmi @Override public void makeUserCabinetAdmin(PerunSession sess, User user) { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.CABINETADMIN.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.CABINETADMIN.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (RuntimeException e) { throw new InternalErrorException(e); } @@ -765,8 +780,8 @@ public void addVoRole(PerunSession sess, String role, Vo vo, User user) throws A throw new IllegalArgumentException("Role " + role + " cannot be set on VO"); } try { - jdbc.update("insert into authz (user_id, role_id, vo_id) values (?, (select id from roles where name=?), ?)", user.getId(), - role.toLowerCase(), vo.getId()); + jdbc.update("insert into authz (user_id, role_id, vo_id, created_by, created_by_uid) values (?, (select id from roles where name=?), ?, ?, ?)", + user.getId(), role.toLowerCase(), vo.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already " + role + " in vo " + vo, e, user, vo, role); } catch (RuntimeException e) { @@ -780,8 +795,9 @@ public void addVoRole(PerunSession sess, String role, Vo vo, Group group) throws throw new IllegalArgumentException("Role " + role + " cannot be set on VO"); } try { - jdbc.update("insert into authz (role_id, vo_id, authorized_group_id) values ((select id from roles where name=?), ?, ?)", - role.toLowerCase(), vo.getId(), group.getId()); + jdbc.update("insert into authz (role_id, vo_id, authorized_group_id, created_by, created_by_uid)" + + " values ((select id from roles where name=?), ?, ?, ?, ?)", + role.toLowerCase(), vo.getId(), group.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already " + role + " in vo " + vo, e, group, vo, role); } catch (RuntimeException e) { @@ -857,8 +873,8 @@ public void addResourceRole(PerunSession sess, User user, String role, Resource throw new InternalErrorException("Role " + role + " cannot be set on resource."); } try { - jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), - role.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid) values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", user.getId(), + role.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already " + role + " in resource " + resource, e, user, resource, role); } catch (RuntimeException e) { @@ -872,8 +888,9 @@ public void addResourceRole(PerunSession sess, Group group, String role, Resourc throw new IllegalArgumentException("Role " + role + " cannot be set on resource."); } try { - jdbc.update("insert into authz (role_id, resource_id, authorized_group_id, vo_id, facility_id) values ((select id from roles where name=?), ?, ?, ?, ?)", - role.toLowerCase(), resource.getId(), group.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (role_id, resource_id, authorized_group_id, vo_id, facility_id, created_by, created_by_uid)" + + " values ((select id from roles where name=?), ?, ?, ?, ?, ?, ?)", + role.toLowerCase(), resource.getId(), group.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already " + role + " in resource " + resource, e, group, resource, role); } catch (RuntimeException e) { @@ -988,10 +1005,15 @@ public Integer getRoleId(String role) { @Override public void setRole(PerunSession sess, Map mappingOfValues, String role) throws RoleAlreadySetException { - String query = prepareQueryToSetRole(mappingOfValues); + Map genericMappingOfValues = prepareMappingToSetRole(mappingOfValues); + genericMappingOfValues.put("created_by", sess.getPerunPrincipal().getActor()); + genericMappingOfValues.put("created_by_uid", sess.getPerunPrincipal().getUserId()); + SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbc); + insert.withTableName("authz"); + insert.usingColumns(genericMappingOfValues.keySet().toArray(new String[0])); try { - jdbc.update(query); + insert.execute(genericMappingOfValues); } catch (DataIntegrityViolationException e) { throw new RoleAlreadySetException(role); } catch (RuntimeException e) { @@ -1165,12 +1187,8 @@ private MapSqlParameterSource prepareParametersToGetObjectsByUserRoles(User user * @param mappingOfValues from which will be the query created * @return sql query */ - private String prepareQueryToSetRole(Map mappingOfValues) { - String columnsFromMapping; - String valuesFromMapping; - List columnNames = new ArrayList<>(); - List columnValues = new ArrayList<>(); - + private Map prepareMappingToSetRole(Map mappingOfValues) { + Map genericMappingOfValues = new HashMap<>(); for (String columnName : mappingOfValues.keySet()) { if (columnName == null || mappingOfValues.get(columnName) == null) { @@ -1181,14 +1199,10 @@ private String prepareQueryToSetRole(Map mappingOfValues) { if (!matcher.matches()) { throw new InternalErrorException("Cannot create a query to set role, because column name: " + columnName + " contains forbidden characters. Allowed are only [1-9a-zA-Z_]."); } - columnNames.add(columnName); - columnValues.add(mappingOfValues.get(columnName).toString()); + genericMappingOfValues.put(columnName, mappingOfValues.get(columnName)); } - columnsFromMapping = StringUtils.join(columnNames, ","); - valuesFromMapping = StringUtils.join(columnValues, ","); - - return "insert into authz (" + columnsFromMapping + ") values (" + valuesFromMapping + ")"; + return genericMappingOfValues; } /** diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 4e1c5feaa1..309eb8f8e9 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.16 +ALTER TABLE authz ADD COLUMN created_at timestamp default statement_timestamp() not null; +ALTER TABLE authz ADD column created_by varchar default user not null; + 3.2.15 INSERT INTO authz (user_id, role_id) VALUES ((select user_id from user_ext_sources where login_ext='perun' and ext_sources_id=(select id from ext_sources where name='INTERNAL' and type='cz.metacentrum.perun.core.impl.ExtSourceInternal')),(select id from roles where name='perunadmin')) ON CONFLICT DO NOTHING; UPDATE configurations set value='3.2.15' WHERE property='DATABASE VERSION'; diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java index 8ac4f80c4b..ae519a8beb 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java @@ -1200,6 +1200,7 @@ public void setRoleGroupAdminSucceedsForVoAdmin() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); @@ -1249,6 +1250,7 @@ public void getVoAdminGroupsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testGroup, testVo, Role.VOADMIN); @@ -1287,6 +1289,7 @@ public void getVoDirectRichAdminsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testUser, testVo, Role.VOADMIN); @@ -1331,6 +1334,7 @@ public void getVoRichAdminsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testUser, testVo, Role.VOADMIN); diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index 00dd4f86c6..f66604ed88 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.15 (don't forget to update insert statement at the end of file) +-- database version 3.2.16 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -1597,6 +1597,8 @@ create table authz ( modified_by_uid integer, authorized_group_id integer, --identifier of whole authorized group security_team_id integer, --identifier of security team + created_at timestamp default statement_timestamp() not null, + created_by varchar default user not null, constraint authz_role_fk foreign key (role_id) references roles(id), constraint authz_user_fk foreign key (user_id) references users(id), constraint authz_authz_group_fk foreign key (authorized_group_id) references groups(id), @@ -2014,7 +2016,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.15'); +insert into configurations values ('DATABASE VERSION','3.2.16'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); From 6de84b7e71e1141b91058fadb1fd4abcfa320389 Mon Sep 17 00:00:00 2001 From: Adam Bodnar Date: Tue, 1 Aug 2023 14:15:37 +0200 Subject: [PATCH 23/30] feat(core): attribute modul for mfaEnforceSettings * Added semantic and syntactic checks --- ..._attribute_def_def_mfaEnforceSettings.java | 190 ++++++++++++++++++ ...ribute_def_def_mfaEnforceSettingsTest.java | 181 +++++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java create mode 100644 perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java new file mode 100644 index 0000000000..6520c5f626 --- /dev/null +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java @@ -0,0 +1,190 @@ +package cz.metacentrum.perun.core.impl.modules.attributes; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import cz.metacentrum.perun.core.implApi.modules.attributes.UserAttributesModuleAbstract; +import cz.metacentrum.perun.core.implApi.modules.attributes.UserAttributesModuleImplApi; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +/** + * Check if value of mfaEnforceSetting attribute is valid + */ +public class urn_perun_user_attribute_def_def_mfaEnforceSettings extends UserAttributesModuleAbstract implements UserAttributesModuleImplApi { + + + /** + * Attribute value should be a valid JSON. + * These specific values are allowed: + * empty string or null + * {"all":true} + * {"include_categories":["str1","str2"]} + * {"include_categories":["str1","str2"],"exclude_rps":["rp1","rp2"]} + * + * @param perunSession PerunSession + * @param user User + * @param attribute Attribute of the user. + * + * @throws WrongAttributeValueException + */ + @Override + public void checkAttributeSyntax(PerunSessionImpl perunSession, User user, Attribute attribute) throws WrongAttributeValueException { + String val = attribute.valueAsString(); + + // Null or empty string are allowed + if (val == null || val.isEmpty()) return; + + // Should be string in valid JSON format + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(val); + + // Check "all" + if (root.has("all") && root.size() == 1) return; + + int validNodes = 0; + // Check "include_categories" + if (root.has("include_categories")) { + isStringArrayNode(root.get("include_categories"), "include_categories"); + validNodes += 1; + + // Check "exclude_rps" + if (root.has("exclude_rps")) { + isStringArrayNode(root.get("exclude_rps"), "exclude_rps"); + validNodes += 1; + } + } + + // Check for NO additional nodes + if (root.size() == validNodes) return; + + throw new WrongAttributeValueException( + "Attribute value " + val + " has incorrect format." + + " Allowed values are:" + + " empty string or null," + + " {\"all\":true}," + + " {\"include_categories\":[\"str1\",\"str2\"]}," + + " {\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}"); + } catch (JsonProcessingException e) { + throw new WrongAttributeValueException("Attribute value " + val + " is not a valid JSON."); + } + } + + /** + * Checks that node is an array and all values are strings + * + * @param node JsonNode + * @throws WrongAttributeValueException + */ + private void isStringArrayNode(JsonNode node, String name) throws WrongAttributeValueException { + // Check property is valid array + if (node.isArray()) { + // Check all items of array are string like + for (Iterator it = node.elements(); it.hasNext(); ) { + JsonNode value = it.next(); + if (!value.isTextual()) { + throw new WrongAttributeValueException("Property '" + name + "' has non textual value " + value); + } + } + } else { + throw new WrongAttributeValueException("Property '" + name + "' is not an array."); + } + } + + /** + * The following restrictions are placed on the attribute value: + * {"include_categories":["str1","str2"]} str1, str2 is an existing key in the entityless attribute mfaCategories + * {"include_categories":["str1","str2"],"exclude_rps":["rp1","rp2"]} str1, str2 is an existing key in the entityless attribute mfaCategories and rp1, rp2 must exist inside the category + * + * @param perunSession PerunSession + * @param user User + * @param attribute Attribute of the user. + * + * @throws WrongReferenceAttributeValueException + * @throws WrongAttributeAssignmentException + */ + @Override + public void checkAttributeSemantics(PerunSessionImpl perunSession, User user, Attribute attribute) throws WrongReferenceAttributeValueException, WrongAttributeAssignmentException { + String val = attribute.valueAsString(); + if (val == null || val.isEmpty()) return; + + Set includeCategories = null; + Set excludeRps = null; + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(val); + + if (root.has("all")) { + if (root.get("all").asBoolean()) return; + throw new WrongAttributeAssignmentException("Property 'all' is only valid with true ({\"all\":true})."); + } + + // Initialize included categories to check + includeCategories = new HashSet<>(); + fillSet(root.get("include_categories"), includeCategories); + + // Initialize excluded rps to check (optional) + excludeRps = new HashSet<>(); + if (root.has("exclude_rps")) { + fillSet(root.get("exclude_rps"), excludeRps); + } + } catch (JsonProcessingException e) { + throw new WrongAttributeAssignmentException("Attribute " + attribute + "is incorrectly assigned."); + } + + Attribute mfaCategories = perunSession.getPerunBl().getAttributesManagerBl().getEntitylessAttributes(perunSession, "mfaCategories").get(0); + String mfaSettingsValue = mfaCategories.valueAsString(); + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode mfaCategoriesNode = mapper.readTree(mfaSettingsValue).get("categories"); + + // Iterate through categories and check that all included categories exist + for (Iterator> catIt = mfaCategoriesNode.fields(); catIt.hasNext(); ) { + Map.Entry catEntry = catIt.next(); + + boolean checkRps = includeCategories.remove(catEntry.getKey()); + if (checkRps) { + // Iterate through rps and check that all excluded rps exist + JsonNode rps = catEntry.getValue().get("rps"); + for (Iterator> rpsIt = rps.fields(); rpsIt.hasNext(); ) { + Map.Entry rp = rpsIt.next(); + excludeRps.remove(rp.getKey()); + } + } + } + + // Both sets should be empty + if (!includeCategories.isEmpty()) { + throw new WrongReferenceAttributeValueException("Categories " + includeCategories + " do not exist inside mfaCategories attribute."); + } + if (!excludeRps.isEmpty()) { + throw new WrongReferenceAttributeValueException("Rps " + excludeRps + " do not exist inside included categories in mfaCategories attribute."); + } + } catch (JsonProcessingException e) { + throw new WrongAttributeAssignmentException("Attribute " + mfaCategories + "is incorrectly assigned."); + } + } + + /** + * Add all elements of node to a set + * + * @param node JsonNode + * @param set HashSet + */ + private void fillSet(JsonNode node, Set set) { + for (Iterator it = node.elements(); it.hasNext(); ) { + JsonNode next = it.next(); + set.add(next.textValue()); + } + } +} diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java new file mode 100644 index 0000000000..ec1a061efb --- /dev/null +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java @@ -0,0 +1,181 @@ +package cz.metacentrum.perun.core.impl.modules.attributes; + +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class urn_perun_user_attribute_def_def_mfaEnforceSettingsTest { + private static urn_perun_user_attribute_def_def_mfaEnforceSettings classInstance; + private static PerunSessionImpl session; + private static User user; + private static Attribute attributeToCheck; + + @Before + public void setUp() { + classInstance = new urn_perun_user_attribute_def_def_mfaEnforceSettings(); + session = mock(PerunSessionImpl.class, RETURNS_DEEP_STUBS); + user = new User(); + attributeToCheck = new Attribute(); + + + Attribute mockMfaCategories = new Attribute(); + mockMfaCategories.setValue("{\"categories\":" + + " {" + + " \"cat1\":" + + " {" + + " \"label\": {\"en\": \"cat1_en_label\"}," + + " \"rps\":" + + " {" + + " \"cat1_rps1\": {\"en\":\"cat1_rps1_en_label\"}," + + " \"cat1_rps2\": {\"en\":\"cat1_rps2_en_label\"}" + + " }" + + " }," + + " \"cat2\":" + + " {" + + " \"label\": {\"en\": \"cat2_en_label\"}," + + " \"rps\":" + + " {" + + " \"cat2_rps1\": {\"en\":\"cat2_rps1_en_label\"}" + + " }" + + " }" + + " }" + + "}"); + when(session.getPerunBl().getAttributesManagerBl().getEntitylessAttributes(any(), any())) + .thenReturn(Collections.singletonList(mockMfaCategories)); + } + + @Test + public void testAttributeSyntaxNotValidJSON() { + System.out.println("testAttributeSyntaxNotValidJSON()"); + attributeToCheck.setValue("plain string"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Attribute value " + attributeToCheck.getValue() + " is not a valid JSON." , errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxNull() throws Exception { + System.out.println("testAttributeSyntaxNull()"); + attributeToCheck.setValue(null); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxEmpty() throws Exception { + System.out.println("testAttributeSyntaxEmpty()"); + attributeToCheck.setValue(""); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxAllTrue() throws Exception { + System.out.println("testAttributeSyntaxAllTrue()"); + attributeToCheck.setValue("{\"all\": true}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategories() throws Exception { + System.out.println("testAttributeSyntaxCategories()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"]}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategoriesRps() throws Exception { + System.out.println("testAttributeSyntaxCategoriesRps()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategoryNotArray() { + System.out.println("testAttributeSyntaxCategoryNotArray()"); + attributeToCheck.setValue("{\"include_categories\":\"str\"}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Property 'include_categories' is not an array.", errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxCategoryValueNotString() { + System.out.println("testAttributeSyntaxCategoryValueNotString()"); + attributeToCheck.setValue("{\"include_categories\":[\"str\", []]}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Property 'include_categories' has non textual value []", errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxWrongFormat() { + System.out.println("testAttributeSyntaxWrongFormat()"); + attributeToCheck.setValue("{\"all\":true,\"include_categories\":[\"str1\",\"str2\"]}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals( + "Attribute value {\"all\":true,\"include_categories\":[\"str1\",\"str2\"]} has incorrect format." + + " Allowed values are:" + + " empty string or null," + + " {\"all\":true}, {\"include_categories\":[\"str1\",\"str2\"]}," + + " {\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}", + errorWithoutId(e.getMessage())); + } + } + + @Test + public void testCheckAttributeSemanticsCorrect() throws Exception { + System.out.println("testCheckAttributeSemanticsCorrect()"); + attributeToCheck.setValue("{\"include_categories\":[\"cat1\",\"cat2\"],\"exclude_rps\":[\"cat1_rps2\",\"cat2_rps1\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + @Test(expected = WrongReferenceAttributeValueException.class) + public void testCheckAttributeSemanticsWrongCategoryReference() throws Exception { + System.out.println("testCheckAttributeSemanticsWrongCategoryReference()"); + attributeToCheck.setValue("{\"include_categories\":[\"cat1\",\"wrong_cat\"],\"exclude_rps\":[\"cat1_rps2\",\"cat2_rps1\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + @Test(expected = WrongReferenceAttributeValueException.class) + public void testCheckAttributeSemanticsWrongRpsReference() throws Exception { + System.out.println("testCheckAttributeSemanticsWrongRpsReference()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"wrong_rps\",\"rp2\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + private String errorWithoutId(String msg) { + return msg.substring(msg.indexOf(":") + 1).trim(); + } +} From 3434c8c931760047fe0bbc27ccae57bc28da1d72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:47:15 +0000 Subject: [PATCH 24/30] chore(deps): update dependency org.codehaus.cargo:cargo-maven3-plugin to v1.10.9 --- perun-rpc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perun-rpc/pom.xml b/perun-rpc/pom.xml index b4d38adf73..4a316bceae 100644 --- a/perun-rpc/pom.xml +++ b/perun-rpc/pom.xml @@ -34,7 +34,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.8 + 1.10.9 tomcat9x From da3d1eb24553b11933d259cd33438fe9287a710b Mon Sep 17 00:00:00 2001 From: HejdaJakub Date: Tue, 1 Aug 2023 17:16:17 +0200 Subject: [PATCH 25/30] feat(core): allow to set attribute action as globally critical * We used to have only the option to set attribute action as critical only for critical objects. * Now we have an option to set any attribute definition as globally critical, so it will not depend on the related objects. BREAKING CHANGE: column 'global' was added to the attribute_critical_actions table Database changelog: ALTER TABLE attribute_critical_actions ADD COLUMN global boolean default false not null; UPDATE configurations SET value='3.2.17' WHERE property='DATABASE VERSION'; --- .../perun/core/api/AttributeRules.java | 8 +++-- perun-base/src/test/resources/test-schema.sql | 5 +-- .../perun/core/api/AttributesManager.java | 3 +- .../perun/core/bl/AttributesManagerBl.java | 13 ++++++- .../core/blImpl/AttributesManagerBlImpl.java | 21 ++++++++--- .../core/blImpl/AuthzResolverBlImpl.java | 4 ++- .../core/entry/AttributesManagerEntry.java | 4 +-- .../core/impl/AttributesManagerImpl.java | 21 +++++++++-- .../implApi/AttributesManagerImplApi.java | 13 ++++++- .../src/main/resources/postgresChangelog.txt | 4 +++ ...AttributesManagerEntryIntegrationTest.java | 36 +++++++++++++++---- perun-db/postgres.sql | 5 +-- perun-openapi/openapi.yml | 12 +++++-- .../rpc/methods/AttributesManagerMethod.java | 16 ++++++++- 14 files changed, 135 insertions(+), 30 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java index b46dcbef88..ed55013bc0 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java @@ -1,7 +1,9 @@ package cz.metacentrum.perun.core.api; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -12,7 +14,7 @@ public class AttributeRules { private List attributePolicyCollections; - private List criticalActions = new ArrayList<>(); + private Map criticalActions = new HashMap<>(); public AttributeRules() { } @@ -29,11 +31,11 @@ public void setAttributePolicyCollections(List attrib this.attributePolicyCollections = attributePolicyCollections; } - public List getCriticalActions() { + public Map getCriticalActions() { return criticalActions; } - public void setCriticalActions(List criticalActions) { + public void setCriticalActions(Map criticalActions) { this.criticalActions = criticalActions; } diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 708ed6ffff..9dbaab915e 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.16 (don't forget to update insert statement at the end of file) +-- database version 3.2.17 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -331,6 +331,7 @@ create table attribute_policies ( create table attribute_critical_actions ( attr_id integer not null, --identifier of attribute (attr_names.id) action attribute_action not null, --action on attribute (READ/WRITE) + global boolean default false not null, --action is critical globally for all objects constraint attrcritops_pk primary key (attr_id, action), constraint attrcritops_attr_fk foreign key (attr_id) references attr_names (id) on delete cascade ); @@ -1907,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.16'); +insert into configurations values ('DATABASE VERSION','3.2.17'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); insert into membership_types (id, membership_type, description) values (2, 'INDIRECT', 'Member is added indirectly through UNION relation'); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java index 6a846fa815..a17b281f07 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java @@ -4140,12 +4140,13 @@ Map getEntitylessAttributesWithKeys(PerunSession sess, String * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action * @throws PrivilegeException insufficient permissions */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException, PrivilegeException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException, PrivilegeException; /** * Returns list of definitions of IdP attributes that are filled to fedInfo diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java index 03508e1fc8..9c84a91bc7 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java @@ -4819,6 +4819,16 @@ void mergeAttributesValues(PerunSession sess, Member member, List att */ boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** + * Checks if the action is critical on given attribute for all objects. + * + * @param sess session + * @param attr attribute definition + * @param action critical action + * @return true if action is globally critical, false otherwise + */ + boolean isAttributeActionGloballyCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** * Returns critical actions on given attribute. * @@ -4836,11 +4846,12 @@ void mergeAttributesValues(PerunSession sess, Member member, List att * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException; /** * Returns list of definitions of IdP attributes that are filled to fedInfo 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 db70dd775b..c8f6608fe4 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 @@ -147,6 +147,7 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import static cz.metacentrum.perun.core.api.AttributeAction.READ; import static cz.metacentrum.perun.core.api.AttributeAction.WRITE; @@ -2506,7 +2507,7 @@ private AttributeDefinition createAttribute(PerunSession sess, AttributeDefiniti // mark WRITE action on this attribute as critical try { - setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true); + setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false); } catch (RelationExistsException | RelationNotExistsException ignored) { } @@ -8733,7 +8734,14 @@ public List getAttributePolicyCollections(PerunSessio @Override public AttributeRules getAttributeRules(PerunSession sess, int attributeId) { AttributeRules attrRules = new AttributeRules(getAttributesManagerImpl().getAttributePolicyCollections(sess, attributeId)); - attrRules.setCriticalActions(getAttributesManagerImpl().getCriticalAttributeActions(sess, attributeId)); + Map actionMap = getAttributesManagerImpl() + .getCriticalAttributeActions(sess, attributeId) + .stream() + .collect(Collectors.toMap( + attrAction -> attrAction, + attrAction -> getAttributesManagerImpl().isAttributeActionGloballyCritical(sess, attributeId, attrAction) + )); + attrRules.setCriticalActions(actionMap); return attrRules; } @@ -8860,14 +8868,19 @@ public boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition return getAttributesManagerImpl().isAttributeActionCritical(sess, attr, action); } + @Override + public boolean isAttributeActionGloballyCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action) { + return getAttributesManagerImpl().isAttributeActionGloballyCritical(sess, attr.getId(), action); + } + @Override public List getCriticalAttributeActions(PerunSession sess, int attrId) { return getAttributesManagerImpl().getCriticalAttributeActions(sess, attrId); } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException { - getAttributesManagerImpl().setAttributeActionCriticality(sess, attr, action, critical); + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException { + getAttributesManagerImpl().setAttributeActionCriticality(sess, attr, action, critical, global); } @Override diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index bdbb976e99..ba59fb22af 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -1697,7 +1697,9 @@ public static boolean isMfaAuthorizedForAttribute(PerunSession sess, AttributeDe return principalMfa || updatePrincipalMfa(sess); } - return principalMfa || !isAnyObjectMfaCritical(sess, objects) || updatePrincipalMfa(sess); + boolean globallyCriticalAction = ((PerunBl) sess.getPerun()).getAttributesManagerBl().isAttributeActionGloballyCritical(sess, attrDef, actionType); + + return principalMfa || (!isAnyObjectMfaCritical(sess, objects) && !globallyCriticalAction) || updatePrincipalMfa(sess); } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java index 75c13b2310..c8b1a9985c 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java @@ -4644,7 +4644,7 @@ public GraphDTO getModulesDependenciesGraph(PerunSession session, GraphTextForma } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException, PrivilegeException { + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException, PrivilegeException { Utils.checkPerunSession(sess); // Authorization @@ -4652,7 +4652,7 @@ public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition throw new PrivilegeException("setAttributeActionCriticality"); } - attributesManagerBl.setAttributeActionCriticality(sess, attr, action, critical); + attributesManagerBl.setAttributeActionCriticality(sess, attr, action, critical, global); } @Override diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java index 6baf947a46..7b422bc3db 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java @@ -4168,6 +4168,16 @@ public boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition } } + @Override + public boolean isAttributeActionGloballyCritical(PerunSession sess, int attrId, AttributeAction action) { + try { + return 0 < jdbc.queryForInt("select count(*) from attribute_critical_actions where attr_id=? and action=?::attribute_action and global=true", + attrId, action.toString()); + } catch (RuntimeException ex) { + throw new InternalErrorException(ex); + } + } + @Override public List getCriticalAttributeActions(PerunSession sess, int attrId) { try { @@ -4178,13 +4188,18 @@ public List getCriticalAttributeActions(PerunSession sess, int } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException { + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException { try { if (critical) { - if (isAttributeActionCritical(sess, attr, action)) { + boolean globalCriticalityChanged = isAttributeActionGloballyCritical(sess, attr.getId(), action) != global; + + if (isAttributeActionCritical(sess, attr, action) && !globalCriticalityChanged) { throw new RelationExistsException("Attribute " + attr.getName() + " is already critical on " + action + " action."); } - jdbc.update("insert into attribute_critical_actions (attr_id, action) values (?,?::attribute_action)", attr.getId(), action.toString()); + + jdbc.update("insert into attribute_critical_actions (attr_id, action, global) values (?,?::attribute_action,?) " + + "on conflict(attr_id, action) do update set global=?", + attr.getId(), action.toString(), global, global); } else { if (0 == jdbc.update("delete from attribute_critical_actions where attr_id=? and action=?::attribute_action", attr.getId(), action.toString())) { throw new RelationNotExistsException("Attribute " + attr.getName() + " is not critical on " + action + " action."); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java index 581c8f458f..6c862d0a6e 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java @@ -2775,6 +2775,16 @@ public interface AttributesManagerImplApi { */ boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** + * Checks if the action is critical on given attribute for all objects. + * + * @param sess session + * @param attrId attribute definition id + * @param action critical action + * @return true if action is globally critical, false otherwise + */ + boolean isAttributeActionGloballyCritical(PerunSession sess, int attrId, AttributeAction action); + /** * Returns critical actions on given attribute. * @@ -2792,11 +2802,12 @@ public interface AttributesManagerImplApi { * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException; /** * Returns list of all possible namespaces. diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 309eb8f8e9..82b351f680 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.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'; + 3.2.16 ALTER TABLE authz ADD COLUMN created_at timestamp default statement_timestamp() not null; ALTER TABLE authz ADD column created_by varchar default user not null; diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java index 15abaf2715..6bee2a4d7d 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java @@ -10013,9 +10013,9 @@ public void unmarkWriteActionAsCritical() throws Exception { Attribute attribute = setUpMemberGroupAttribute().get(0); assertTrue("Writing to attribute should be critical by default", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.WRITE)); - assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).containsExactly(AttributeAction.valueOf("WRITE")); + assertTrue(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions().containsKey(AttributeAction.WRITE)); - perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, false); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, false, false); assertFalse("Writing to attribute should not be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.WRITE)); assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).isEmpty(); } @@ -10029,13 +10029,37 @@ public void setCriticalOperations() throws Exception { member = setUpMember(); Attribute attribute = setUpMemberGroupAttribute().get(0); - assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true)); - assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false)); + assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false)); + assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false, false)); assertFalse(perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); - perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true, false); assertTrue("Reading attribute should be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); - assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions() + .keySet()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + } + + @Test + public void setGloballyCriticalOperations() throws Exception { + System.out.println(CLASS_NAME + "setGloballyCriticalOperations"); + + vo = setUpVo(); + group = setUpGroup(); + member = setUpMember(); + Attribute attribute = setUpMemberGroupAttribute().get(0); + + assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false)); + assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false, false)); + + assertFalse(perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true, true); + assertTrue("This attribute should be marked as globally critical", perun.getAttributesManagerBl().isAttributeActionGloballyCritical(sess, attribute, AttributeAction.READ)); + assertTrue("Reading attribute should be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); + + Map criticalActionMap = perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions(); + assertThat(criticalActionMap.keySet()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + assertTrue(criticalActionMap.containsKey(AttributeAction.WRITE) && !criticalActionMap.get(AttributeAction.WRITE)); + assertTrue(criticalActionMap.containsKey(AttributeAction.READ) && criticalActionMap.get(AttributeAction.READ)); } @Test diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index f66604ed88..c2923381a6 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.16 (don't forget to update insert statement at the end of file) +-- database version 3.2.17 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -329,6 +329,7 @@ create table attribute_policies ( create table attribute_critical_actions ( attr_id integer not null, --identifier of attribute (attr_names.id) action attribute_action not null, --action on attribute (READ/WRITE) + global boolean default false not null, --action is critical globally for all objects constraint attrcritops_pk primary key (attr_id, action), constraint attrcritops_attr_fk foreign key (attr_id) references attr_names (id) on delete cascade ); @@ -2016,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.16'); +insert into configurations values ('DATABASE VERSION','3.2.17'); -- 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 ff7b210f67..c6469fad84 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -389,9 +389,14 @@ components: items: $ref: '#/components/schemas/AttributePolicyCollection' criticalActions: - type: array - items: - $ref: '#/components/schemas/AttributeAction' + type: object + description: "Represents types of actions (READ, WRITE) that users can perform upon attributes and if they are critical globally or not." + additionalProperties: + type: boolean + example: { + READ: false, + WRITE: true, + } AuditEvent: type: object @@ -7714,6 +7719,7 @@ paths: - $ref: '#/components/parameters/attributeDefinitionId' - {name: action, in: query, schema: { $ref: '#/components/schemas/AttributeAction' }, required: true} - {name: critical, description: "if action should be marked as critical", schema: { type: boolean }, in: query, required: true} + - {name: global, description: "if action should be globally critical (for all objects)", schema: { type: boolean }, in: query, required: false} responses: '200': $ref: '#/components/responses/VoidResponse' diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java index 3b1158d6b8..ac029f0cea 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java @@ -4086,6 +4086,19 @@ public GraphDTO call(ApiCaller ac, Deserializer parms) throws PrivilegeException * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ + /*# + * Marks the action on attribute as critical, which may require additional authentication of user + * performing that action on attribute. + * + * @param sess session + * @param attributeDefinition attribute definition id + * @param action critical action + * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects + * + * @throws RelationExistsException if trying to mark already critical action + * @throws RelationNotExistsException if trying to unmark not critical action + */ setAttributeActionCriticality { @Override public Void call(ApiCaller ac, Deserializer parms) throws PerunException { @@ -4094,7 +4107,8 @@ public Void call(ApiCaller ac, Deserializer parms) throws PerunException { ac.getSession(), ac.getAttributeDefinitionById(parms.readInt("attributeDefinition")), AttributeAction.valueOf(parms.readString("action").toUpperCase()), - parms.readBoolean("critical")); + parms.readBoolean("critical"), + parms.contains("global") ? parms.readBoolean("global") : false); return null; } }, From a85de7144b8022e316aa585f4fbbc8c202bf4bf7 Mon Sep 17 00:00:00 2001 From: HejdaJakub Date: Tue, 8 Aug 2023 10:36:34 +0200 Subject: [PATCH 26/30] fixup! feat(core): extend authz table with audit attributes * Update DB configuration DEPLOYMENT NOTE: authz table was updated ALTER TABLE authz ADD COLUMN created_at timestamp default statement_timestamp() not null; ALTER TABLE authz ADD column created_by varchar default user not null; UPDATE configurations set value='3.2.16' WHERE property='DATABASE VERSION'; --- perun-core/src/main/resources/postgresChangelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 309eb8f8e9..f0ed887804 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -9,6 +9,7 @@ 3.2.16 ALTER TABLE authz ADD COLUMN created_at timestamp default statement_timestamp() not null; ALTER TABLE authz ADD column created_by varchar default user not null; +UPDATE configurations set value='3.2.16' WHERE property='DATABASE VERSION'; 3.2.15 INSERT INTO authz (user_id, role_id) VALUES ((select user_id from user_ext_sources where login_ext='perun' and ext_sources_id=(select id from ext_sources where name='INTERNAL' and type='cz.metacentrum.perun.core.impl.ExtSourceInternal')),(select id from roles where name='perunadmin')) ON CONFLICT DO NOTHING; From 60eccd088924090fc78b71857f2cd4a286c39e94 Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Tue, 18 Jul 2023 20:41:53 +0200 Subject: [PATCH 27/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20new=20RPC=20method?= =?UTF-8?q?=20membersManager/sendUsernameReminder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementation of method to send user email with a reminder of the username for the given namespace --- ...ificationMemberMailNotExistsException.java | 23 +++ .../PasswordResetMailNotExistsException.java | 22 --- perun-base/src/main/resources/perun-roles.yml | 12 ++ .../perun/core/api/MembersManager.java | 47 +++-- .../perun/core/bl/MembersManagerBl.java | 12 ++ .../core/blImpl/MembersManagerBlImpl.java | 172 +++++++++--------- .../perun/core/entry/MembersManagerEntry.java | 110 +++++------ .../cz/metacentrum/perun/core/impl/Utils.java | 35 ++++ perun-openapi/openapi.yml | 17 ++ .../rpc/methods/MembersManagerMethod.java | 22 +++ 10 files changed, 298 insertions(+), 174 deletions(-) create mode 100644 perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java delete mode 100644 perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java new file mode 100644 index 0000000000..a454f8ca36 --- /dev/null +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java @@ -0,0 +1,23 @@ +package cz.metacentrum.perun.core.api.exceptions; + +/** + * This exception is thrown when the user doesn't have the chosen attribute for mail set, + * where the notification should be sent to. + * + * @author Radoslav Čerhák + * @author Dominik František Bučík + */ +public class NotificationMemberMailNotExistsException extends PerunException { + + public NotificationMemberMailNotExistsException(String message) { + super(message); + } + + public NotificationMemberMailNotExistsException(String message, Throwable cause) { + super(message, cause); + } + + public NotificationMemberMailNotExistsException(Throwable cause) { + super(cause); + } +} diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java deleted file mode 100644 index 8154957c12..0000000000 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java +++ /dev/null @@ -1,22 +0,0 @@ -package cz.metacentrum.perun.core.api.exceptions; - -/** - * This exception is thrown when the user doesn't have the chosen attribute for mail set, - * where password reset link should be send to. - * - * @author Radoslav Čerhák - */ -public class PasswordResetMailNotExistsException extends PerunException { - - public PasswordResetMailNotExistsException(String message) { - super(message); - } - - public PasswordResetMailNotExistsException(String message, Throwable cause) { - super(message, cause); - } - - public PasswordResetMailNotExistsException(Throwable cause) { - super(cause); - } -} diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 62b2cd3a28..2d00d4e654 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -3332,6 +3332,18 @@ perun_policies: include_policies: - default_policy + sendUsernameReminderEmail_Member_String_String_String_policy: + policy_roles: + - VOADMIN: Vo + - SPONSORSHIP: Member + - PASSWORDRESETMANAGER: + - PROXY: + include_policies: + - default_policy + mfa_rules: + - MFA: User + - MFA: Member + sendPasswordResetLinkEmail_Member_String_String_String_String_policy: policy_roles: - VOADMIN: Vo diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java index fc9ad017f7..860d25a3f9 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java @@ -22,7 +22,7 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; -import cz.metacentrum.perun.core.api.exceptions.PasswordResetMailNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; @@ -1138,17 +1138,32 @@ public interface MembersManager { Date getNewExtendMembership(PerunSession sess, Member member) throws MemberNotExistsException; /** - * Returns the date to which will be extended member's expiration time. - * - * @param sess - * @param vo - * @param loa - * @return date - * @throws InternalErrorException - * @throws VoNotExistsException - * @throws ExtendMembershipException - */ - Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws VoNotExistsException, ExtendMembershipException; + * Returns the date to which will be extended member's expiration time. + * + * @param sess + * @param vo + * @param loa + * @return date + * @throws InternalErrorException + * @throws VoNotExistsException + * @throws ExtendMembershipException + */ + Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws VoNotExistsException, ExtendMembershipException; + + /** + * Send mail to user's preferred email address with reminder of the username in the given namespace. + * + * @param sess PerunSession + * @param member Member to get user to send link mail to + * @param namespace namespace to change password in (member must have login in it) + * @param mailAttributeUrn urn of the attribute with stored mail + * @param language language of the message + * @throws InternalErrorException + * @throws PrivilegeException If not VO admin of member + * @throws MemberNotExistsException If member not exists + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. + */ + void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Send mail to user's preferred email address with link for non-authz password reset. @@ -1163,9 +1178,9 @@ public interface MembersManager { * @throws InternalErrorException * @throws PrivilegeException If not VO admin of member * @throws MemberNotExistsException If member not exists - * @throws PasswordResetMailNotExistsException If the attribute with stored mail is not filled. + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. */ - void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException; + void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Send mail to user's preferred email address with link for non-authz account activation. @@ -1180,9 +1195,9 @@ public interface MembersManager { * @throws InternalErrorException * @throws PrivilegeException If not VO admin of member * @throws MemberNotExistsException If member not exists - * @throws PasswordResetMailNotExistsException If the attribute with stored mail is not filled. + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. */ - void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException; + void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Creates a new sponsored Member and its User. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java index 1f6da2b580..5119f40085 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java @@ -1465,6 +1465,18 @@ public interface MembersManagerBl { */ List filterOnlyAllowedAttributes(PerunSession sess, List richMembers, Group group, boolean useContext); + /** + * Send mail to user's preferred email address with username for the given namespace. + * + * @param sess PerunSession + * @param member Member to get user to send mail to + * @param namespace Namespace for username/login (member must have login in this namespace) + * @param mailAddress mail address where email will be sent + * @param language language of the message + * @throws InternalErrorException + */ + void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAddress, String language); + /** * Send mail to user's preferred email address with link for non-authz password reset. * Correct authz information is stored in link's URL. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java index 4f97105185..a5554c8414 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java @@ -108,6 +108,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; +import org.springframework.util.StringUtils; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; @@ -2493,55 +2494,46 @@ private boolean isMemberInGracePeriod(Map membershipExpirationRu } @Override - public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, - String mailAddress, String language) { + public void sendUsernameReminderEmail(PerunSession sess, Member member, final String namespace, String mailAddress, + String language) { User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - List logins = perunBl.getAttributesManagerBl().getLogins(sess, user); - boolean found = false; - for (Attribute a : logins) { - if (a.getFriendlyNameParameter().equals(namespace)) found = true; - } - if (!found) - throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); + String logAction = "account activation"; - String subject; - try { - Attribute subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - if (subject == null) { - subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for subject will be used - log.error("There is missing attribute with subject template for password reset in specific namespace.", ex); - subject = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { + throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); } - String message; - try { - Attribute messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - if (message == null) { - messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for message will be used - log.error("There is missing attribute with message template for password reset in specific namespace.", ex); - message = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); + String attrNameBase = "usernameReminderMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); + + Utils.sendUsernameReminderEmail(user, mailAddress, login, namespace, message, subject); + } + + @Override + public void sendPasswordResetLinkEmail(PerunSession sess, Member member, final String namespace, String url, + String mailAddress, String language) { + + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + + String logAction = "account activation"; + + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { + throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); } + String attrNameBase = "nonAuthzPwdResetMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); + int validationWindow = BeansUtils.getCoreConfig().getPwdresetValidationWindow(); LocalDateTime validityTo = LocalDateTime.now().plusHours(validationWindow); @@ -2550,61 +2542,29 @@ public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String } @Override - public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, - String mailAddress, String language) { + public void sendAccountActivationLinkEmail(PerunSession sess, Member member, final String namespace, String url, + String mailAddress, String language) { + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + String logAction = "account activation"; - List logins = perunBl.getAttributesManagerBl().getLogins(sess, user); - boolean found = false; - for (Attribute a : logins) { - if (a.getFriendlyNameParameter().equals(namespace)) found = true; - } - if (!found) + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); - - String subject; - try { - Attribute subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - if (subject == null) { - subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for subject will be used - log.error("There is missing attribute with subject template for account activation in specific namespace.", ex); - subject = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); } - String message; - try { - Attribute messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - if (message == null) { - messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for message will be used - log.error("There is missing attribute with message template for account activation in specific namespace.", ex); - message = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } + String attrNameBase = "nonAuthzAccActivationMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); int validationWindow = BeansUtils.getCoreConfig().getAccountActivationValidationWindow(); LocalDateTime validityTo = LocalDateTime.now().plusHours(validationWindow); //IMPORTANT: we are using the same requests for password reset and account activation UUID uuid = getMembersManagerImpl().storePasswordResetRequest(sess, user, namespace, mailAddress, validityTo); - String userLogin = getUserLogin(sess, user, namespace); - Utils.sendAccountActivationEmail(user, mailAddress, userLogin, namespace, url, uuid, message, subject, validityTo); + Utils.sendAccountActivationEmail(user, mailAddress, login, namespace, url, uuid, message, subject, validityTo); } @Override @@ -3729,4 +3689,46 @@ private String getUserLogin(PerunSession sess, User user, String namespace) { throw new InternalErrorException("Failed to get namespace login for user: " + user, e); } } + + /** + * Resolve email template (subject/message) from an entityless attribute. + * @param sess Perun session + * @param attributeName friendly name of the attribute (namespace of entityless is assigned in the method) + * @param language Language of the template. If fails to find, default EN template will be looked up. + * @return Found template for given language. If fails, tries to find for default EN. If fails, returns null. + */ + private String getEmailMessagePartFromEntitylessAttribute(PerunSession sess, + String attributeName, + String language, + String logAction) + { + String template = null; + try { + attributeName = AttributesManager.NS_ENTITYLESS_ATTR_DEF + ':' + attributeName; + try { + Attribute templateAttribute = perunBl.getAttributesManagerBl().getAttribute( + sess, language, attributeName + ); + template = templateAttribute.valueAsString(); + } catch (AttributeNotExistsException ex) { + //If attribute not exists, log it and use null instead - default template for message will be used + log.error("There is missing attribute with message template for {} in specific namespace.", logAction, ex); + } + if (!StringUtils.hasText(template)) { + try { + Attribute templateAttribute = perunBl.getAttributesManagerBl().getAttribute( + sess, "en", attributeName + ); + template = templateAttribute.valueAsString(); + } catch (AttributeNotExistsException ex) { + //If attribute not exists, log it and use null instead - default template for message will be used + log.error("There is missing attribute with email message template for {} in specific namespace.", + logAction, ex); + } + } + } catch (WrongAttributeAssignmentException ex) { + throw new InternalErrorException(ex); + } + return template; + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index abcff123b7..f8b880698a 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -48,7 +48,7 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; -import cz.metacentrum.perun.core.api.exceptions.PasswordResetMailNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; @@ -67,6 +67,7 @@ import cz.metacentrum.perun.core.api.SponsoredUserData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import java.util.Optional; import java.util.stream.Collectors; @@ -1194,47 +1195,36 @@ public Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws } @Override - public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException { - + public void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { Utils.checkPerunSession(sess); getMembersManagerBl().checkMemberExists(sess, member); // Authorization - if (!AuthzResolver.authorizedInternal(sess, "sendPasswordResetLinkEmail_Member_String_String_String_String_policy", member)) { - throw new PrivilegeException(sess, "sendPasswordResetLinkEmail"); + if (!AuthzResolver.authorizedInternal(sess, "sendUsernameReminderEmail_Member_String_String_String_policy", member)) { + throw new PrivilegeException(sess, "sendUsernameReminder"); } - //check if attribute exists, throws AttributeNotExistsException - Attribute mailAttribute = null; - AttributeDefinition ad = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); + getMembersManagerBl().sendUsernameReminderEmail(sess, member, namespace, mailAddress, language); + } + @Override + public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { - try { - if (ad.getEntity().equals("user")) { - User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); - } - if (ad.getEntity().equals("member")) { - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); - } - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } + Utils.checkPerunSession(sess); + getMembersManagerBl().checkMemberExists(sess, member); - if (mailAttribute == null) { - throw new InternalErrorException("MailAttribute should not be null."); - } - String mailAddress = mailAttribute.valueAsString(); - if (mailAddress == null) { - throw new PasswordResetMailNotExistsException("Member " + member.getId() + " doesn't have the attribute " + - mailAttributeUrn + " set."); + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "sendPasswordResetLinkEmail_Member_String_String_String_String_policy", member)) { + throw new PrivilegeException(sess, "sendPasswordResetLinkEmail"); } + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); getMembersManagerBl().sendPasswordResetLinkEmail(sess, member, namespace, url, mailAddress, language); } @Override - public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException { + public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { Utils.checkPerunSession(sess); getMembersManagerBl().checkMemberExists(sess, member); @@ -1243,30 +1233,7 @@ public void sendAccountActivationLinkEmail(PerunSession sess, Member member, Str throw new PrivilegeException(sess, "sendAccountActivationLinkEmail"); } - //check if attribute exists, throws AttributeNotExistsException - Attribute mailAttribute = null; - AttributeDefinition attributeDefinition = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); - - try { - if (attributeDefinition.getEntity().equals("user")) { - User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); - } - if (attributeDefinition.getEntity().equals("member")) { - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); - } - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } - - if (mailAttribute == null) { - throw new InternalErrorException("MailAttribute should not be null."); - } - String mailAddress = mailAttribute.valueAsString(); - if (mailAddress == null) { - throw new PasswordResetMailNotExistsException("Member " + member.getId() + " doesn't have the attribute " + - mailAttributeUrn + " set."); - } + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); getMembersManagerBl().sendAccountActivationLinkEmail(sess, member, namespace, url, mailAddress, language); } @@ -1823,6 +1790,47 @@ private MemberWithSponsors convertMemberToMemberWithSponsors(PerunSession sess, return memberWithSponsors; } + /** + * Extract mail address from the given attribute and given member object. + * @param sess Perun session + * @param member Member object + * @param mailAttributeUrn URN of the attribute which should contain mail address. Assumes the attribute is of + * type string. + * @return Non-blank attribute value which is expected to be mail address + * @throws AttributeNotExistsException If the attribute specified by mailAttributeUrn parameter does not exist + * @throws NotificationMemberMailNotExistsException If the attribute has empty or blank value + */ + private String getMailAddressFromAttribute(PerunSession sess, Member member, String mailAttributeUrn) + throws AttributeNotExistsException, NotificationMemberMailNotExistsException + { + //check if attribute exists, throws AttributeNotExistsException + Attribute mailAttribute = null; + AttributeDefinition ad = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); + + try { + if (ad.getEntity().equals("user")) { + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); + } + if (ad.getEntity().equals("member")) { + mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); + } + } catch (WrongAttributeAssignmentException ex) { + throw new InternalErrorException(ex); + } + + if (mailAttribute == null) { + throw new InternalErrorException("MailAttribute should not be null."); + } + String mailAddress = mailAttribute.valueAsString(); + if (!StringUtils.hasText(mailAddress)) { + throw new NotificationMemberMailNotExistsException( + "Member " + member.getId() + " doesn't have the attribute " + mailAttributeUrn + " set." + ); + } + return mailAddress; + } + /** * Gets the membersManagerBl for this instance. * diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java index c87efc3679..9f6e74576f 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java @@ -1071,6 +1071,41 @@ public static void sendValidationEmail(User user, String url, String email, UUID sendEmail(subject, content, email); } + /** + * Sends email with reminder of the username in the specified namespace to the user + * @param user user to send notification for + * @param email user's email to send notification to + * @param login user's login which will be used in message, if tag {@code {login}} is used. + * @param namespace namespace to reset password in + * @param messageTemplate message of the email (uses default if null) + * @param subject subject of the email (uses default if null) + */ + public static void sendUsernameReminderEmail(User user, String email, String login, String namespace, String messageTemplate, String subject) { + String instanceName = BeansUtils.getCoreConfig().getInstanceName(); + + String defaultSubject = "[" + instanceName + "] Username reminder for namespace: " + namespace; + String defaultBody = "Dear " + user.getDisplayName() + ",\n\n" + + "\n\nWe've received request to remind you your username for namespace \"" + namespace + "\"." + + "\nYour username is: " + login + + "\n\nMessage is automatically generated." + + "\n----------------------------------------------------------------" + + "\nPerun - Identity & Access Management System"; + + Map subjectParametersToReplace = new HashMap<>(); + subjectParametersToReplace.put("{instanceName}", instanceName); + subjectParametersToReplace.put("{namespace}", namespace); + subject = prepareSubjectOfEmail(subject, defaultSubject, subjectParametersToReplace); + + Map bodyParametersToReplace = new HashMap<>(); + bodyParametersToReplace.put("{displayName}", user.getDisplayName()); + bodyParametersToReplace.put("{namespace}", namespace); + bodyParametersToReplace.put("{login}", login); + + messageTemplate = prepareBodyOfEmail(messageTemplate, defaultBody, bodyParametersToReplace); + + sendEmail(subject, messageTemplate, email); + } + /** * Sends email with link to non-authz account activation where user can activate his account by setting a password. * @param user user to send notification for diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index c6469fad84..c27664956b 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -11002,6 +11002,23 @@ paths: default: $ref: '#/components/responses/ExceptionResponse' + /urlinjsonout/membersManager/sendUsernameReminderEmail: + post: + tags: + - MembersManager + operationId: sendUsernameReminderEmail + summary: Send mail to user's preferred email address with a reminder of the username for the given namespace. + parameters: + - $ref: '#/components/parameters/memberId' + - $ref: '#/components/parameters/namespace' + - { name: emailAttributeURN, schema: { type: string }, in: query, required: true, description: "urn of the attribute with stored mail" } + - { name: language, schema: { type: string }, in: query, required: true, description: "language of the message" } + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + /urlinjsonout/membersManager/sendPasswordResetLinkEmail: post: tags: diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java index 01f8e1b078..c71707d4a6 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java @@ -1962,6 +1962,28 @@ public String call(ApiCaller ac, Deserializer parms) throws PerunException { } }, + /*# + * Send reminder of username in the given namespace to user's preferred email address. + * + * @param member int Member to get user to send link mail to + * @param namespace String Namespace to change password in (member must have login in it) + * @param emailAttributeURN String URN of the attribute with stored mail + * @param language String 2-char language code of the message + */ + sendUsernameReminderEmail { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + ac.getMembersManager().sendUsernameReminderEmail( + ac.getSession(), + ac.getMemberById(parms.readInt("member")), + parms.readString("namespace"), + parms.readString("emailAttributeURN"), + parms.readString("language") + ); + return null; + } + }, + /*# * Send mail to user's preferred email address with link for non-authz password reset. * Correct authz information is stored in link's URL. From 9d52d5857d0b253218fc2e86a39b85a3df88e534 Mon Sep 17 00:00:00 2001 From: mattjoke Date: Mon, 17 Jul 2023 09:01:37 +0200 Subject: [PATCH 28/30] feat(core): filter getMembersPage * getMembersPage now adhere to policy * added filter `filter-getMembersPage_policy` policy * added getPolicyByName to AuthzResolver * added tests to accompany this change * added support for VOADMIN and VOOBSERVER --- .../perun/core/api/MembersOrderColumn.java | 26 +- perun-base/src/main/resources/perun-roles.yml | 11 + perun-base/src/test/resources/test-roles.yml | 33 ++ .../perun/core/api/MembersManager.java | 19 +- .../perun/core/bl/MembersManagerBl.java | 15 +- .../core/blImpl/AuthzResolverBlImpl.java | 30 ++ .../core/blImpl/MembersManagerBlImpl.java | 10 +- .../perun/core/entry/MembersManagerEntry.java | 10 +- .../perun/core/impl/AuthzResolverImpl.java | 37 ++ .../perun/core/impl/MembersManagerImpl.java | 75 +++- .../core/implApi/AuthzResolverImplApi.java | 17 + .../core/implApi/MembersManagerImplApi.java | 14 +- .../MembersManagerEntryIntegrationTest.java | 355 +++++++++++++++++- 13 files changed, 638 insertions(+), 14 deletions(-) diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java index 2f916a291a..8a93462d04 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java @@ -14,13 +14,20 @@ public enum MembersOrderColumn { NAME( ", users.first_name, users.last_name ", "", + ", users.last_name, users.first_name", query -> "users.last_name " + getLangSql(query) + query.getOrder().getSqlValue() + ", " + "users.first_name " + getLangSql(query) + query.getOrder().getSqlValue() ), ID("", "", query -> "members.id " + query.getOrder().getSqlValue()), - STATUS("","", query -> "members.status " + query.getOrder().getSqlValue()), - GROUP_STATUS("", "", query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), + STATUS("", + "", + "", + query -> "members.status " + query.getOrder().getSqlValue()), + GROUP_STATUS("", + "", + ", groups_members.group_id, groups_members.source_group_id, groups_members.membership_type, groups_members.source_group_status", + query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), // 1. user preferred mail, 2. member mail EMAIL( @@ -33,6 +40,7 @@ public enum MembersOrderColumn { "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:preferredMail') ", + ", usrvals.attr_value, memvals.attr_value ", query -> "usrvals.attr_value " + query.getOrder().getSqlValue() + ", " + "memvals.attr_value " + query.getOrder().getSqlValue() ), @@ -48,6 +56,7 @@ public enum MembersOrderColumn { "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:organization') ", + ", usrvals.attr_value, memvals.attr_value ", query -> "memvals.attr_value " + query.getOrder().getSqlValue() + ", " + "usrvals.attr_value " + query.getOrder().getSqlValue() ); @@ -55,10 +64,19 @@ public enum MembersOrderColumn { private final Function orderBySqlFunction; private final String selectSql; private final String joinSql; + private final String groupbySql; + + MembersOrderColumn(String selectSql, String joinSql, String groupbySql, Function sqlFunction) { + this.selectSql = selectSql; + this.joinSql = joinSql; + this.groupbySql = groupbySql; + this.orderBySqlFunction = sqlFunction; + } MembersOrderColumn(String selectSql, String joinSql, Function sqlFunction) { this.selectSql = selectSql; this.joinSql = joinSql; + this.groupbySql = ""; this.orderBySqlFunction = sqlFunction; } @@ -74,6 +92,10 @@ public String getSqlJoin() { return this.joinSql; } + public String getSqlGroupBy() { + return this.groupbySql; + } + private static String getLangSql(MembersPageQuery query) { return ""; // TODO add support for other languages diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 2d00d4e654..8cd14c18aa 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -3630,6 +3630,17 @@ perun_policies: include_policies: - default_policy + filter-getMembersPage_policy: + policy_roles: + - GROUPADMIN: + - GROUPOBSERVER: + - GROUPMEMBERSHIPMANAGER: + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: + include_policies: + - default_policy + updateSponsorshipValidity_Member_User_LocalDate: policy_roles: - VOADMIN: Vo diff --git a/perun-base/src/test/resources/test-roles.yml b/perun-base/src/test/resources/test-roles.yml index d1d24a0e71..8058c49c00 100644 --- a/perun-base/src/test/resources/test-roles.yml +++ b/perun-base/src/test/resources/test-roles.yml @@ -205,5 +205,38 @@ perun_policies: - PROXY: include_policies: [] + test_filter-getMembersPage_policy: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: + include_policies: + - default_policy + + test_filter-getMembersPage_policy-vo: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: Vo + - VOOBSERVER: + include_policies: + - default_policy + + test_filter-getMembersPage_policy-voobserver: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: Vo + include_policies: + - default_policy + perun_roles_management: {} ... \ No newline at end of file diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java index 860d25a3f9..ee926c9b27 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java @@ -24,6 +24,7 @@ import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; @@ -1521,7 +1522,23 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @throws GroupNotExistsException if there is no such query group * @throws PrivilegeException insufficient permission */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException; + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException; + + /** + * Get page of members from the given vo, with the given attributes, based on policy. + * + * @param sess session + * @param vo vo + * @param query query with page information + * @param attrNames attribute names + * @param policy policy to use + * @return page of requested rich members + * @throws VoNotExistsException if there is no such vo + * @throws GroupNotExistsException if there is no such query group + * @throws PrivilegeException insufficient permission + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException; + /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java index 5119f40085..57d915bfe6 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java @@ -42,6 +42,7 @@ import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; @@ -1797,7 +1798,19 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @param attrNames attribute names * @return page of requested rich members */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames); + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws PolicyNotExistsException; + + /** + * Get page of members from the given vo, with the given attributes, based on policy. + * + * @param sess session + * @param vo vo + * @param query query with page information + * @param attrNames attribute names + * @param policy policy to use + * @return page of requested rich members + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws PolicyNotExistsException; /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 4050b2e86b..65b6f9713d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -2288,6 +2288,16 @@ public static boolean isVoObserver(PerunSession sess) { return sess.getPerunPrincipal().getRoles().hasRole(Role.VOOBSERVER); } + /** + * Returns true if the perun principal inside the perun session is Perun Observer. + * + * @param sess perun session + * @return true if the perun principal is top group creator. + */ + public static boolean isPerunObserver(PerunSession sess) { + return sess.getPerunPrincipal().getRoles().hasRole(Role.PERUNOBSERVER); + } + /** * Returns true if the perun principal inside the perun session is top group creator. * @@ -2308,6 +2318,16 @@ public static boolean isPerunAdmin(PerunSession sess) { return sess.getPerunPrincipal().getRoles().hasRole(Role.PERUNADMIN); } + /** + * Returns true if perun principal is Vo admin or Vo observer of specific Vo. + * @param sess - perun session + * @param vo -specific vo + * @return bolean + **/ + public static boolean isVoAdminOrObserver(PerunSession sess, Vo vo) { + return authzResolverImpl.isVoAdminOrObserver(sess, vo); + } + /** * Get all principal role names. * @@ -4472,4 +4492,14 @@ private static boolean checkAuthValidityForMFA(PerunSession sess) { private static boolean sessionHasMfa(PerunSession sess) { return sess.getPerunPrincipal().getAdditionalInformations().containsKey(ACR_MFA); } + + /** + * Return id of the role by its name. + * + * @param name - name of the role + * @return - id of the role + */ + public static int getRoleIdByName(String name) { + return authzResolverImpl.getRoleIdByName(name); + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java index a5554c8414..df871a1ae1 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java @@ -82,6 +82,7 @@ import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.RelationExistsException; import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; import cz.metacentrum.perun.core.api.exceptions.RoleManagementRulesNotExistsException; @@ -2992,8 +2993,8 @@ public List findMembers(PerunSession sess, Vo vo, String searchString, b } @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) { - Paginated paginatedMembers = membersManagerImpl.getMembersPage(sess, vo, query); + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws PolicyNotExistsException { + Paginated paginatedMembers = membersManagerImpl.getMembersPage(sess, vo, query, policy); List richMembers = convertMembersToRichMembers(sess, paginatedMembers.getData()); List attrDefs = new ArrayList<>(); @@ -3019,6 +3020,11 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag paginatedMembers.getTotalCount()); } + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws PolicyNotExistsException { + return getMembersPage(sess, vo, query, attrNames, null); + } + @Override public void updateSponsorshipValidity(PerunSession sess, Member sponsoredMember, User sponsor, LocalDate newValidity) throws SponsorshipDoesNotExistException { diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index f8b880698a..836a8ad6a1 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -50,6 +50,7 @@ import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; @@ -1630,7 +1631,12 @@ public void removeSponsors(PerunSession sess, Member sponsoredMember, List } @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException { + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException { + return getMembersPage(sess, vo, query, attrNames, null); + } + + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException { Utils.checkPerunSession(sess); perunBl.getVosManagerBl().checkVoExists(sess, vo); @@ -1649,7 +1655,7 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag throw new IllegalArgumentException("Group status cannot be used to sort VO members."); } - Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames); + Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames, policy); if (query.getGroupId() == null) { result.setData(getPerunBl().getMembersManagerBl().filterOnlyAllowedAttributes(sess, result.getData())); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java index 5ab9b338eb..667e5d924c 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java @@ -8,6 +8,7 @@ import cz.metacentrum.perun.core.api.Member; import cz.metacentrum.perun.core.api.MemberGroupStatus; import cz.metacentrum.perun.core.api.Pair; +import cz.metacentrum.perun.core.api.Perun; import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.PerunSession; import cz.metacentrum.perun.core.api.Resource; @@ -27,6 +28,7 @@ import cz.metacentrum.perun.core.api.exceptions.RoleManagementRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.RoleNotSetException; import cz.metacentrum.perun.core.api.exceptions.UserNotAdminException; +import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.AuthzResolverImplApi; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -1286,4 +1288,39 @@ private String prepareSelectQueryString(Map mappingOfValues) { return StringUtils.join(listOfConditions, " and "); } + + /** + * Returns role id based on its name + * + * @param name - name of the role + * @return role id + */ + @Override + public int getRoleIdByName(String name) { + try { + return jdbc.queryForInt("SELECT id FROM roles WHERE name=?", name.toLowerCase()); + } catch (RuntimeException e) { + throw new InternalErrorException(e); + } + } + + /** + * Returns true if the user in session is vo admin or vo observer of specific vo + * + * @param sess - session + * @param vo - vo + * @return + */ + @Override + public boolean isVoAdminOrObserver(PerunSession sess, Vo vo) { + try { + var query = jdbc.query("SELECT 1 FROM authz WHERE user_id=? AND vo_id=? AND (role_id=? OR role_id=?)", + (rs, i) -> true, + sess.getPerunPrincipal().getUserId(), vo.getId(), AuthzResolverBlImpl.getRoleIdByName(Role.VOADMIN), AuthzResolverBlImpl.getRoleIdByName(Role.VOOBSERVER)); + return !query.isEmpty(); + } catch (InternalErrorException e) { + log.error("Error during checking if user is vo admin of vo {}", vo, e); + } + return false; + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java index 4695e919d0..1af9cc8e3d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java @@ -10,7 +10,9 @@ import cz.metacentrum.perun.core.api.NamespaceRules; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.MembersPageQuery; +import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.RichMember; +import cz.metacentrum.perun.core.api.Role; import cz.metacentrum.perun.core.api.Sponsorship; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.Pair; @@ -32,9 +34,11 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordDeletionFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordOperationTimeoutException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.bl.DatabaseManagerBl; import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.MembersManagerImplApi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -732,8 +736,14 @@ public List findMembers(PerunSession sess, Vo vo, String searchString, b return new ArrayList<>(members); } + + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) throws PolicyNotExistsException { + return getMembersPage(sess, vo, query, null); + } + @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) { + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, String policy) throws PolicyNotExistsException { Map> attributesToSearchBy = Utils.getDividedAttributes(); MapSqlParameterSource namedParams = Utils.getMapSqlParameterSourceToSearchUsersOrMembers(query.getSearchString(), attributesToSearchBy); @@ -744,17 +754,28 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQue namedParams.addValue("voId", vo.getId()); namedParams.addValue("offset", query.getOffset()); namedParams.addValue("limit", query.getPageSize()); + namedParams.addValue("userId", sess.getPerunPrincipal().getUserId()); String statusesQueryString = getVoStatusSQLConditionForMembersPage(query, namedParams); String groupStatusesQueryString = getGroupStatusSQLConditionForMembersPage(query, namedParams); + String whereBasedOnThePolicy = getWhereConditionBasedOnThePolicy(sess, query, policy, vo); + + String groupByQuery = "GROUP BY members.user_id, members.id"; + if (query.getGroupId() == null) { + groupByQuery += query.getSortColumn().getSqlGroupBy(); + } else { + groupByQuery += ", users.last_name, users.first_name, groups_members.group_id, groups_members.source_group_id, groups_members.membership_type, groups_members.source_group_status"; + } + return namedParameterJdbcTemplate.query( select + - " WHERE members.vo_id = (:voId)" + + whereBasedOnThePolicy + statusesQueryString + groupStatusesQueryString + searchQuery + + groupByQuery+ " ORDER BY " + query.getSortColumn().getSqlOrderBy(query) + " OFFSET (:offset)" + " LIMIT (:limit)" @@ -857,7 +878,8 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { "SELECT " + memberMappingSelectQuery + " ,count(*) OVER() AS total_count" + query.getSortColumn().getSqlSelect() + - " FROM members JOIN users on members.user_id = users.id " + + " FROM members JOIN users ON members.user_id = users.id " + + getSQLBasedOnPolicy() + query.getSortColumn().getSqlJoin(); String groupSelect = @@ -884,6 +906,52 @@ private String getSQLWhereForMembersPage(MembersPageQuery query, MapSqlParameter return " AND " + Utils.prepareSqlWhereForUserMemberSearch(query.getSearchString(), namedParams, false); } + private String getSQLBasedOnPolicy() { + return "LEFT OUTER JOIN (SELECT groups_members.member_id, authz.role_id, authz.vo_id" + + " FROM groups" + + " JOIN authz ON groups.id = authz.group_id" + + " JOIN groups_members ON groups.id = groups_members.group_id" + + " WHERE authz.user_id = (:userId))" + + " AS members_group ON members.id = members_group.member_id AND members.vo_id = members_group.vo_id"; + } + + private String getWhereConditionBasedOnThePolicy(PerunSession sess, MembersPageQuery query, String otherPolicy, Vo vo) throws PolicyNotExistsException { + String defaultWhereCondition = " WHERE members.vo_id = (:voId)"; + PerunPolicy policy = AuthzResolverImpl.getPerunPolicy("filter-getMembersPage_policy"); + if (otherPolicy != null && !otherPolicy.isEmpty()) { + policy = AuthzResolverImpl.getPerunPolicy(otherPolicy); + } + + // Check if user is VO admin in vo + boolean ignoreGroupRelation = AuthzResolverBlImpl.isPerunAdmin(sess) || AuthzResolverBlImpl.isPerunObserver(sess) || AuthzResolverBlImpl.isVoAdminOrObserver(sess, vo); + if (query.getGroupId() != null || ignoreGroupRelation) { + return defaultWhereCondition; + } + + List roles = new ArrayList<>(); + for (Map role : policy.getPerunRoles()) { + for (Map.Entry entry : role.entrySet()) { + // Do nothing + if (entry.getValue() == null) continue; + if (entry.getValue().equals("Group")) { + int roleId = AuthzResolverBlImpl.getRoleIdByName(entry.getKey()); + if (roleId == -1) { + log.error("Role {} not found in DB.", entry.getKey()); + continue; + } + roles.add("members_group.role_id=" + roleId); + } + } + } + + if (roles.isEmpty() ) { + return defaultWhereCondition; + } + return " WHERE members.vo_id = (:voId) AND (" + + String.join(" OR ", roles) + + ")"; + } + private String getVoStatusSQLConditionForMembersPage(MembersPageQuery query, MapSqlParameterSource namedParams) { String statusesQueryString = ""; if (query.getStatuses() != null && !query.getStatuses().isEmpty()) { @@ -911,5 +979,4 @@ private String getGroupStatusSQLConditionForMembersPage(MembersPageQuery query, } return groupStatusesQueryString; } - } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java index f2aa3226a3..679f4cbf49 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java @@ -3,6 +3,7 @@ import cz.metacentrum.perun.core.api.Facility; import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.PerunSession; import cz.metacentrum.perun.core.api.Resource; import cz.metacentrum.perun.core.api.SecurityTeam; @@ -651,4 +652,20 @@ public interface AuthzResolverImplApi { */ Set getSecurityTeamsWhereUserIsInRoles(User user, List roles); + /** + * Get role id by its name, returns -1 if role does not exist. + * + * @param name - name of the role + * @return - role id with the given name + */ + int getRoleIdByName(String name); + + /** + * Returns true if the user in session is vo admin or vo observer of specific Vo. + * + * @param sess - session + * @param vo - vo + * @return bolean + */ + boolean isVoAdminOrObserver(PerunSession sess, Vo vo); } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java index 96c3bf2af9..737d2ef42d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java @@ -19,6 +19,7 @@ import cz.metacentrum.perun.core.api.exceptions.MemberAlreadyRemovedException; import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.impl.LoginNamespacesRulesConfigLoader; @@ -408,9 +409,20 @@ public interface MembersManagerImplApi { * @param sess session * @param vo vo * @param query query with page information + * @param policy policy to replace the default one (`filter_getMembersPage-policy`) * @return page of requested rich members */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query); + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, String policy) throws PolicyNotExistsException; + + /** + * Get page of members from the given vo + * + * @param sess session + * @param vo vo + * @param query query with page information + * @return page of requested rich members + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) throws PolicyNotExistsException; /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java index 3997b29a35..70b1fd94ee 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java @@ -4,6 +4,7 @@ import cz.metacentrum.perun.core.api.Attribute; import cz.metacentrum.perun.core.api.AttributeDefinition; import cz.metacentrum.perun.core.api.AttributesManager; +import cz.metacentrum.perun.core.api.AuthzResolver; import cz.metacentrum.perun.core.api.BanOnResource; import cz.metacentrum.perun.core.api.BanOnVo; import cz.metacentrum.perun.core.api.BeansUtils; @@ -21,6 +22,8 @@ import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.NamespaceRules; +import cz.metacentrum.perun.core.api.PerunClient; +import cz.metacentrum.perun.core.api.PerunPrincipal; import cz.metacentrum.perun.core.api.RichUser; import cz.metacentrum.perun.core.api.SortingOrder; import cz.metacentrum.perun.core.api.Paginated; @@ -40,6 +43,7 @@ import cz.metacentrum.perun.core.api.Validation; import cz.metacentrum.perun.core.api.Vo; import cz.metacentrum.perun.core.api.VosManager; +import cz.metacentrum.perun.core.api.exceptions.AlreadyAdminException; import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsorException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsoredMemberException; @@ -51,6 +55,7 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParseUserNameException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; @@ -58,6 +63,7 @@ import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; +import cz.metacentrum.perun.core.impl.AuthzRoles; import cz.metacentrum.perun.core.implApi.modules.attributes.AbstractMembershipExpirationRulesModule; import cz.metacentrum.perun.core.api.SponsoredUserData; import org.junit.Before; @@ -74,7 +80,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.VO_EXPIRATION_RULES_ATTR; @@ -3354,6 +3359,346 @@ public void getGroupMembersPageWithMemberGroupAttribute() throws Exception { } + @Test + public void getMemberPageBasedOnPolicy() throws Exception { + System.out.println(CLASS_NAME + "getMemberPageBasedOnPolicy"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member4 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group, Role.GROUPADMIN); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(2); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId()); + } + @Test + public void getMembersPageBasedOnPolicyNoDuplicities() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyNoDuplicities"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.GROUPADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.GROUPOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(2); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member3.getId(), member4.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyPerunObserver() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, null, Role.PERUNOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyVoAdmin() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.VOADMIN); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy-vo"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyVoObserver() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.VOOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy-voobserver"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + @Test public void getAllMembers_membersFromTwoVos() throws Exception { System.out.println(CLASS_NAME + "getAllMembers_membersFromTwoVos"); @@ -3561,6 +3906,14 @@ private Vo setUpVo(String name) throws Exception { return perun.getVosManagerBl().createVo(sess, new Vo(0, name, name)); } + private Member setupMemberInSession(PerunSession sess, Vo vo, String lastName, String firstName) throws Exception { + User user = new User(); + user.setFirstName(firstName); + user.setLastName(lastName); + user = perun.getUsersManagerBl().createUser(sess, user); + return perun.getMembersManagerBl().createMember(sess, vo, user); + } + private Member setUpMember(Vo vo, String lastName, String firstName) throws Exception { User user = new User(); user.setFirstName(firstName); From 17ab1ab64275003d21a8bfdaa4a09829393f34c0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 10:43:55 +0000 Subject: [PATCH 29/30] fix(deps): update dependency com.google.apis:google-api-services-admin-directory to directory_v1-rev20230802-2.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06ce49bfdb..856aaf4b56 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 1.6 0.5.10 9.1.22 - directory_v1-rev20230516-2.0.0 + directory_v1-rev20230802-2.0.0 2.2.21.Final 3.2.10.Final 1.1.0.GA From b0dd03373a499a3765c90609dcbab35a3b1df461 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:18:44 +0000 Subject: [PATCH 30/30] chore(deps): update commitlint monorepo to v17.7.0 --- package-lock.json | 745 ++++++++++------------------------------------ 1 file changed, 160 insertions(+), 585 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68b93fa55b..451a797f2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,14 +57,14 @@ } }, "node_modules/@commitlint/cli": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.6.7.tgz", - "integrity": "sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.0.tgz", + "integrity": "sha512-28PNJaGuBQZNoz3sd+6uO3b4+5PY+vWzyBfy5JOvFB7QtoZVXf2FYTQs5VO1cn7yAd3y9/0Rx0x6Vx82W/zhuA==", "dev": true, "dependencies": { "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/lint": "^17.7.0", + "@commitlint/load": "^17.7.0", "@commitlint/read": "^17.5.1", "@commitlint/types": "^17.4.4", "execa": "^5.0.0", @@ -81,31 +81,17 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz", - "integrity": "sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz", + "integrity": "sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^5.0.0" + "conventional-changelog-conventionalcommits": "^6.1.0" }, "engines": { "node": ">=v14" } }, - "node_modules/@commitlint/config-conventional/node_modules/conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@commitlint/config-validator": { "version": "17.6.7", "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz", @@ -120,17 +106,17 @@ } }, "node_modules/@commitlint/cz-commitlint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.6.7.tgz", - "integrity": "sha512-mX1gKRJYzOgbBDVpvlDlLJIbF2dReT2L410eTcnV3DK8AJ5vvkhuXXRPN3jyqO2FVbtYzZDht6noHMhPGFDVlA==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.7.0.tgz", + "integrity": "sha512-292rzapJaQ6WjgcY+KnjChQsWFo7cnPcz9qFT5nUWznDCbZG9B8bGNLyOY2OEdNdI/7+E/nAaJp97rXu091uBg==", "dev": true, "dependencies": { "@commitlint/ensure": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/load": "^17.7.0", "@commitlint/types": "^17.4.4", "chalk": "^4.1.0", "lodash.isplainobject": "^4.0.6", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">=v14" @@ -320,27 +306,27 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz", - "integrity": "sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", + "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", "dev": true, "dependencies": { "@commitlint/types": "^17.4.4", - "semver": "7.5.2" + "semver": "7.5.4" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/lint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.6.7.tgz", - "integrity": "sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", + "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.6.7", - "@commitlint/parse": "^17.6.7", - "@commitlint/rules": "^17.6.7", + "@commitlint/is-ignored": "^17.7.0", + "@commitlint/parse": "^17.7.0", + "@commitlint/rules": "^17.7.0", "@commitlint/types": "^17.4.4" }, "engines": { @@ -348,25 +334,22 @@ } }, "node_modules/@commitlint/load": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.6.7.tgz", - "integrity": "sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.0.tgz", + "integrity": "sha512-jHKErVteyENYKkUiESNE6JYbaU4Sx58HXqv09/p7cKHzTtfyperlRvyqqTnxS8bas5Jjg4MP3MDZwdBrgRm8lw==", "dev": true, "dependencies": { "@commitlint/config-validator": "^17.6.7", "@commitlint/execute-rule": "^17.4.0", "@commitlint/resolve-extends": "^17.6.7", "@commitlint/types": "^17.4.4", - "@types/node": "*", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "resolve-from": "^5.0.0" }, "engines": { "node": ">=v14" @@ -452,14 +435,14 @@ } }, "node_modules/@commitlint/parse": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.6.7.tgz", - "integrity": "sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", + "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", "dev": true, "dependencies": { "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" }, "engines": { "node": ">=v14" @@ -499,9 +482,9 @@ } }, "node_modules/@commitlint/rules": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.6.7.tgz", - "integrity": "sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", + "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", "dev": true, "dependencies": { "@commitlint/ensure": "^17.6.7", @@ -690,43 +673,6 @@ "node": ">=8" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -848,36 +794,6 @@ "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@semantic-release/error": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", @@ -1493,36 +1409,6 @@ "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", @@ -1725,30 +1611,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, "node_modules/@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -1756,10 +1618,11 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", - "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", - "dev": true + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true, + "peer": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1767,27 +1630,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -1871,12 +1713,6 @@ "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2435,16 +2271,15 @@ } }, "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/conventional-changelog-conventionalcommits": { @@ -2500,23 +2335,21 @@ } }, "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, "dependencies": { "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" }, "bin": { "conventional-commits-parser": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/core-util-is": { @@ -2526,9 +2359,9 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "dependencies": { "import-fresh": "^3.2.1", @@ -2538,30 +2371,28 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=v16" }, "peerDependencies": { "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", - "typescript": ">=3" + "cosmiconfig": ">=8.2", + "typescript": ">=4" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2728,15 +2559,6 @@ "node": ">=8" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3918,6 +3740,15 @@ "node": ">= 0.6.0" } }, + "node_modules/jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4279,12 +4110,6 @@ "node": ">=10" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -8162,16 +7987,6 @@ "node": ">=6" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8914,9 +8729,9 @@ } }, "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -9360,49 +9175,6 @@ "node": ">=8" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", @@ -9426,6 +9198,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9501,12 +9274,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9542,9 +9309,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9672,15 +9439,6 @@ "node": ">=12" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", @@ -9722,14 +9480,14 @@ } }, "@commitlint/cli": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.6.7.tgz", - "integrity": "sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.0.tgz", + "integrity": "sha512-28PNJaGuBQZNoz3sd+6uO3b4+5PY+vWzyBfy5JOvFB7QtoZVXf2FYTQs5VO1cn7yAd3y9/0Rx0x6Vx82W/zhuA==", "dev": true, "requires": { "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/lint": "^17.7.0", + "@commitlint/load": "^17.7.0", "@commitlint/read": "^17.5.1", "@commitlint/types": "^17.4.4", "execa": "^5.0.0", @@ -9740,25 +9498,12 @@ } }, "@commitlint/config-conventional": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz", - "integrity": "sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz", + "integrity": "sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==", "dev": true, "requires": { - "conventional-changelog-conventionalcommits": "^5.0.0" - }, - "dependencies": { - "conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - } - } + "conventional-changelog-conventionalcommits": "^6.1.0" } }, "@commitlint/config-validator": { @@ -9772,17 +9517,17 @@ } }, "@commitlint/cz-commitlint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.6.7.tgz", - "integrity": "sha512-mX1gKRJYzOgbBDVpvlDlLJIbF2dReT2L410eTcnV3DK8AJ5vvkhuXXRPN3jyqO2FVbtYzZDht6noHMhPGFDVlA==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.7.0.tgz", + "integrity": "sha512-292rzapJaQ6WjgcY+KnjChQsWFo7cnPcz9qFT5nUWznDCbZG9B8bGNLyOY2OEdNdI/7+E/nAaJp97rXu091uBg==", "dev": true, "requires": { "@commitlint/ensure": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/load": "^17.7.0", "@commitlint/types": "^17.4.4", "chalk": "^4.1.0", "lodash.isplainobject": "^4.0.6", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "dependencies": { "ansi-styles": { @@ -9918,47 +9663,44 @@ } }, "@commitlint/is-ignored": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz", - "integrity": "sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", + "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", "dev": true, "requires": { "@commitlint/types": "^17.4.4", - "semver": "7.5.2" + "semver": "7.5.4" } }, "@commitlint/lint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.6.7.tgz", - "integrity": "sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", + "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", "dev": true, "requires": { - "@commitlint/is-ignored": "^17.6.7", - "@commitlint/parse": "^17.6.7", - "@commitlint/rules": "^17.6.7", + "@commitlint/is-ignored": "^17.7.0", + "@commitlint/parse": "^17.7.0", + "@commitlint/rules": "^17.7.0", "@commitlint/types": "^17.4.4" } }, "@commitlint/load": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.6.7.tgz", - "integrity": "sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.0.tgz", + "integrity": "sha512-jHKErVteyENYKkUiESNE6JYbaU4Sx58HXqv09/p7cKHzTtfyperlRvyqqTnxS8bas5Jjg4MP3MDZwdBrgRm8lw==", "dev": true, "requires": { "@commitlint/config-validator": "^17.6.7", "@commitlint/execute-rule": "^17.4.0", "@commitlint/resolve-extends": "^17.6.7", "@commitlint/types": "^17.4.4", - "@types/node": "*", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "resolve-from": "^5.0.0" }, "dependencies": { "ansi-styles": { @@ -10019,14 +9761,14 @@ "dev": true }, "@commitlint/parse": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.6.7.tgz", - "integrity": "sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", + "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", "dev": true, "requires": { "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" } }, "@commitlint/read": { @@ -10057,9 +9799,9 @@ } }, "@commitlint/rules": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.6.7.tgz", - "integrity": "sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", + "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", "dev": true, "requires": { "@commitlint/ensure": "^17.6.7", @@ -10189,37 +9931,6 @@ } } }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10311,29 +10022,6 @@ "import-from": "^4.0.0", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" - }, - "dependencies": { - "conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "requires": { - "compare-func": "^2.0.0" - } - }, - "conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - } - } } }, "@semantic-release/error": { @@ -10754,27 +10442,6 @@ "read-pkg-up": "^10.0.0" }, "dependencies": { - "conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "requires": { - "compare-func": "^2.0.0" - } - }, - "conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - } - }, "find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", @@ -10907,30 +10574,6 @@ } } }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, "@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -10938,10 +10581,11 @@ "dev": true }, "@types/node": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", - "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", - "dev": true + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true, + "peer": true }, "@types/normalize-package-data": { "version": "2.4.1", @@ -10949,18 +10593,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, "agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -11022,12 +10654,6 @@ "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -11438,13 +11064,12 @@ } }, "conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" } }, "conventional-changelog-conventionalcommits": { @@ -11488,17 +11113,15 @@ } }, "conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, "requires": { "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" } }, "core-util-is": { @@ -11508,9 +11131,9 @@ "dev": true }, "cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "requires": { "import-fresh": "^3.2.1", @@ -11520,17 +11143,13 @@ } }, "cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, - "requires": {} - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "requires": { + "jiti": "^1.19.1" + } }, "cross-spawn": { "version": "7.0.3", @@ -11651,12 +11270,6 @@ "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -12518,6 +12131,12 @@ "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true }, + "jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12818,12 +12437,6 @@ "yallist": "^4.0.0" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -15436,12 +15049,6 @@ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -15948,9 +15555,9 @@ } }, "semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -16293,27 +15900,6 @@ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, "tslib": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", @@ -16330,7 +15916,8 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true + "dev": true, + "peer": true }, "uglify-js": { "version": "3.17.4", @@ -16381,12 +15968,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -16416,9 +15997,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wordwrap": { @@ -16517,12 +16098,6 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, "yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",