diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml index fee61b1631..c3edb12f21 100644 --- a/.github/workflows/semantic-release.yml +++ b/.github/workflows/semantic-release.yml @@ -3,6 +3,7 @@ on: push: branches-ignore: - master + - renovate/* tags-ignore: - v* jobs: diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java index affd29b3d9..77a354ba4f 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java @@ -73,6 +73,7 @@ public void initBeansUtils() { private String userExtSourcesPersistent; private List allowedCorsDomains; private String pdfFontPath; + private boolean notifSendMessages; private String smtpHost; private int smtpPort; private boolean smtpAuth; @@ -598,6 +599,14 @@ public void setPdfFontPath(String pdfFontPath) { this.pdfFontPath = pdfFontPath; } + public boolean getNotifSendMessages() { + return notifSendMessages; + } + + public void setNotifSendMessages(boolean notifSendMessages) { + this.notifSendMessages = notifSendMessages; + } + public String getSmtpHost() { return smtpHost; } diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java index d9804e3bca..2f916a291a 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java @@ -18,7 +18,39 @@ public enum MembersOrderColumn { "users.first_name " + getLangSql(query) + query.getOrder().getSqlValue() ), - ID("", "", query -> "members.id " + query.getOrder().getSqlValue()); + ID("", "", query -> "members.id " + query.getOrder().getSqlValue()), + STATUS("","", query -> "members.status " + query.getOrder().getSqlValue()), + GROUP_STATUS("", "", query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), + + // 1. user preferred mail, 2. member mail + EMAIL( + ", usrvals.attr_value, memvals.attr_value ", + " left join " + + "(select attr_value, member_id, attr_id from member_attr_values) as memvals " + + "on members.id=memvals.member_id and memvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:member:attribute-def:def:mail') " + + " left join " + + "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:preferredMail') ", + query -> "usrvals.attr_value " + query.getOrder().getSqlValue() + ", " + + "memvals.attr_value " + query.getOrder().getSqlValue() + ), + + // 1. member organization, 2. user organization (from IdP) + ORGANIZATION( + ", usrvals.attr_value, memvals.attr_value ", + " left join " + + "(select attr_value, member_id, attr_id from member_attr_values) as memvals " + + "on members.id=memvals.member_id and memvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:member:attribute-def:def:organization') " + + " left join " + + "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:organization') ", + query -> "memvals.attr_value " + query.getOrder().getSqlValue() + ", " + + "usrvals.attr_value " + query.getOrder().getSqlValue() + ); private final Function orderBySqlFunction; private final String selectSql; diff --git a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java index 7cd05da749..7649136cf0 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/registrar/model/Application.java @@ -25,7 +25,7 @@ public static enum AppType { INITIAL, EXTENSION, EMBEDDED } private String extSourceType; private int extSourceLoa = 0; // 0 - by default private User user; - + private String autoApproveError; private String createdBy; private String createdAt; private String modifiedBy; @@ -147,6 +147,10 @@ public String getModifiedAt() { return modifiedAt; } + public String getAutoApproveError() { + return autoApproveError; + } + public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } @@ -163,6 +167,10 @@ public void setModifiedAt(String modifiedAt) { this.modifiedAt = modifiedAt; } + public void setAutoApproveError(String error) { + this.autoApproveError = error; + } + /** * Return bean name as PerunBean does. * @@ -181,6 +189,7 @@ public String toString() { ", fedInfo='" + getFedInfo() + '\'' + ", type='" + getType().toString() + '\'' + ", state='" + getState().toString() + '\'' + + ", autoApproveError='" + getAutoApproveError() + '\'' + ", extSourceName='" + getExtSourceName() + '\'' + ", extSourceType='" + getExtSourceType() + '\'' + ", extSourceLoa='" + getExtSourceLoa() + '\'' + @@ -188,8 +197,7 @@ public String toString() { ", created_at='" + getCreatedAt() + '\'' + ", created_by='" + getCreatedBy() + '\'' + ", modified_at='" + getModifiedAt() + '\'' + - ", modified_by='" + getModifiedBy() + '\'' + - ']'; + ", modified_by='" + getModifiedBy() + '\'' + ']'; } } diff --git a/perun-base/src/main/resources/perun-base.xml b/perun-base/src/main/resources/perun-base.xml index 79a67698c0..8cd2abf3d1 100644 --- a/perun-base/src/main/resources/perun-base.xml +++ b/perun-base/src/main/resources/perun-base.xml @@ -56,6 +56,7 @@ + @@ -163,6 +164,7 @@ https://login.elixir-czech.org/idp/ cz.metacentrum.perun.core.impl.ExtSourceIdp + true localhost 25 false diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 620726265c..166e0628ae 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -149,9 +149,10 @@ perun_roles: # optional word with a dash at the end can be used before the method name. # Example: filter-getAllMembers_Group_policy # -# Each policy is composed of two parts. +# Each policy is composed of two parts and one optional part. # The first one is called policy_roles, which contains privileged roles for this policy. # The second one is include_policies which contains policies which add their policy_roles to this policy. +# Another part, mfa_rules, is optional and is used for defining critical objects of the method. # # The policy_roles is a list of maps, where the relation between list entries is logical OR # and the relation between map entries is logical AND. @@ -185,6 +186,19 @@ perun_roles: # - default_policy # - getAllVos_policy # +# The mfa_rules is a list of rules marking critical objects of the method, which forces user to have valid Multi-Factor +# authentication. The objects themselves need to be marked as critical (in an attribute). Requiring MFA does not need +# to be related to critical objects, the method itself can be marked as critical. The elements of the list are related +# by OR relation meaning if any of the objects is critical, then the method call is considered critical. +# Example, creating subgroup in a group: +# mfa_rules: +# - MFA: Group +# - MFA: Vo +# If parent group or virtual organization is marked as critical (in an attribute), then MFA is required to call this +# method. If +# - MFA: +# would be used instead, the method would require MFA always. +# Some roles (usually system ones) can be exempted from having MFA to call critical operation. perun_policies: default_policy: @@ -1501,6 +1515,26 @@ perun_policies: - MFA: Group - MFA: Vo + addMemberCandidates_Vo_List_Group_policy: + policy_roles: + - GROUPADMIN: Group + - GROUPMEMBERSHIPMANAGER: Group + - SPREGAPPLICATION: + - VOADMIN: Vo + include_policies: + - default_policy + mfa_rules: + - MFA: Group + - MFA: Vo + + addMemberCandidates_Vo_List_policy: + policy_roles: + - VOADMIN: Vo + include_policies: + - default_policy + mfa_rules: + - MFA: Vo + removeMember_Group_Member_policy: policy_roles: - GROUPADMIN: Group diff --git a/perun-base/src/test/resources/test-roles.yml b/perun-base/src/test/resources/test-roles.yml index 4e47a67d55..151a2b1f83 100644 --- a/perun-base/src/test/resources/test-roles.yml +++ b/perun-base/src/test/resources/test-roles.yml @@ -10,9 +10,10 @@ perun_roles: [] # optional word with a dash at the end can be used before the method name. # Example: filter-getAllMembers_Group_policy # -# Each policy is composed of two parts. +# Each policy is composed of two parts and one optional part. # The first one is called policy_roles, which contains privileged roles for this policy. # The second one is include_policies which contains policies which add their policy_roles to this policy. +# Another part, mfa_rules, is optional and is used for defining critical objects of the method. # # The policy_roles is a list of maps, where the relation between list entries is logical OR # and the relation between map entries is logical AND. @@ -46,6 +47,19 @@ perun_roles: [] # - default_policy # - getAllVos_policy # +# The mfa_rules is a list of rules marking critical objects of the method, which forces user to have valid Multi-Factor +# authentication. The objects themselves need to be marked as critical (in an attribute). Requiring MFA does not need +# to be related to critical objects, the method itself can be marked as critical. The elements of the list are related +# by OR relation meaning if any of the objects is critical, then the method call is considered critical. +# Example, creating subgroup in a group: +# mfa_rules: +# - MFA: Group +# - MFA: Vo +# If parent group or virtual organization is marked as critical (in an attribute), then MFA is required to call this +# method. If +# - MFA: +# would be used instead, the method would require MFA always. +# Some roles (usually system ones) can be exempted from having MFA to call critical operation. perun_policies: default_policy: diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 1d7f2fd1ee..f584b42cde 100644 --- a/perun-base/src/test/resources/test-schema.sql +++ b/perun-base/src/test/resources/test-schema.sql @@ -1,4 +1,4 @@ --- database version 3.2.10 (don't forget to update insert statement at the end of file) +-- database version 3.2.11 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -14,7 +14,7 @@ create table vos ( created_by_uid integer, modified_by_uid integer, constraint vo_pk primary key (id), - constraint vo_u unique (name) + constraint vo_u unique (short_name) ); -- USERS - information about user as real person @@ -517,6 +517,7 @@ create table application ( state varchar, --state of application (new/verified/approved/rejected) extSourceLoa integer, --level of assurance of user by external source group_id integer, --identifier of group (groups.id) if application is for group + auto_approve_error varchar, --error that occurred during automatic approval created_at timestamp default statement_timestamp() not null, created_by varchar default user not null, modified_at timestamp default statement_timestamp() not null, @@ -1881,7 +1882,7 @@ create index idx_fk_attr_critops ON attribute_critical_actions(attr_id); create index app_state_idx ON application (state); -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.10'); +insert into configurations values ('DATABASE VERSION','3.2.11'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); insert into membership_types (id, membership_type, description) values (2, 'INDIRECT', 'Member is added indirectly through UNION relation'); diff --git a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java index 6220af1a68..169b30a829 100644 --- a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java +++ b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/PerunCLI.java @@ -121,7 +121,7 @@ private static void call(PerunCommand command, String[] cliArgs) throws ParseExc if (commandLine.hasOption(PERUN_URL_OPTION)) { perunUrl = commandLine.getOptionValue(PERUN_URL_OPTION); } - if (perunUrl == null) perunUrl = "https://perun.cesnet.cz/krb/rpc"; + if (perunUrl == null) perunUrl = "https://perun-api.e-infra.cz/krb/rpc"; // find user and password String user = System.getenv(PERUN_USER_VARIABLE); diff --git a/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java new file mode 100644 index 0000000000..96bdc3d7f7 --- /dev/null +++ b/perun-cli-java/src/main/java/cz/metacentrum/perun/cli/commands/GetFacilityByAttributeWithAttributes.java @@ -0,0 +1,46 @@ +package cz.metacentrum.perun.cli.commands; + +import cz.metacentrum.perun.cli.PerunCLI; +import cz.metacentrum.perun.cli.PerunCommand; +import cz.metacentrum.perun.openapi.model.FacilityWithAttributes; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +import java.util.Arrays; +import java.util.List; + +/** + * Prints owners of facilities having the specified destination. + * + * @author Martin Kuba makub@ics.muni.cz + */ +@SuppressWarnings("unused") +public class GetFacilityByAttributeWithAttributes extends PerunCommand { + + @Override + public String getCommandDescription() { + return "prints attributes of facilities found by attribute value"; + } + + @Override + public void addOptions(Options options) { + options.addOption(Option.builder("a").required(true).hasArg(true).longOpt("attrName").desc("attribute name").build()); + options.addOption(Option.builder("v").required(true).hasArg(true).longOpt("attrValue").desc("attribute value").build()); + options.addOption(Option.builder("r").required(true).hasArg(true).longOpt("returnedAttributeNames").desc("names of returned attributes").build()); + } + + @Override + public void executeCommand(PerunCLI.CommandContext ctx) { + String attributeName = ctx.getCommandLine().getOptionValue("a"); + String attributeValue = ctx.getCommandLine().getOptionValue("v"); + List attrNames = Arrays.asList(ctx.getCommandLine().getOptionValue("r").split(",")); + + List facilities = ctx.getPerunRPC().getFacilitiesManager().getFacilitiesByAttributeWithAttributes(attributeName, attributeValue, attrNames); + + for (FacilityWithAttributes facility : facilities) { + System.out.println(facility); + } + + } + +} diff --git a/perun-cli-python/generate.sh b/perun-cli-python/generate.sh index b18f94dd16..c3f0ae5a02 100755 --- a/perun-cli-python/generate.sh +++ b/perun-cli-python/generate.sh @@ -1,6 +1,6 @@ #!/bin/bash -GENERATOR_VERSION=6.2.1 +GENERATOR_VERSION=6.3.0 if [ ! -f "openapi-generator-cli-$GENERATOR_VERSION.jar" ] ; then wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/$GENERATOR_VERSION/openapi-generator-cli-$GENERATOR_VERSION.jar fi diff --git a/perun-cli-python/perun/cli/getFacilitiesByAttribute.py b/perun-cli-python/perun/cli/getFacilitiesByAttribute.py new file mode 100644 index 0000000000..bbc7beb3c2 --- /dev/null +++ b/perun-cli-python/perun/cli/getFacilitiesByAttribute.py @@ -0,0 +1,40 @@ +from typing import Optional +from typer import Option +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import perun.cli +import typer + +from perun_openapi.model.facility import Facility + + +def main(attr_name: str = Option(..., '-a', '--attributeName', help='attribute name (namespace + : + friendlyName)'), + attr_value: str = Option(..., '-v', '--attributeValue', help='short name of VO'), + sort_by_id: bool = typer.Option(False, '-i', '--orderById', help='order by id'), + sort_by_name: bool = typer.Option(False, '-n', '--orderByName', help='order by short name') + ) -> None: + """ search for facilities by attributeName and attributeValue """ + rpc = perun.cli.rpc + try: + facilities: list[Facility] = rpc.facilities_manager.get_facilities_by_attribute(attr_name, attr_value) + if sort_by_id: + facilities.sort(key=lambda x: x.id) + if sort_by_name: + facilities.sort(key=lambda x: x.name) + console = Console() + # print user + table = Table(title="facilities") + table.add_column("id", justify="right") + table.add_column("name") + table.add_column("description") + for facility in facilities: + table.add_row(str(facility.id), str(facility.name), str(facility.description)) + console.print(table) + + except ApiException as ex: + print('error name:', PerunException(ex).name) + print('error message:', PerunException(ex).message) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py new file mode 100644 index 0000000000..b37790cf3c --- /dev/null +++ b/perun-cli-python/perun/cli/getFacilitiesByAttributeWA.py @@ -0,0 +1,53 @@ +from typing import Optional +from typer import Option +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import perun.cli +import typer + +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.facility import Facility +from perun_openapi.model.facility_with_attributes import FacilityWithAttributes + + +def main( + attr_name: str = Option('urn:perun:facility:attribute-def:def:administratorContact', '-a', '--attributeName', help='attribute name (namespace + : + friendlyName)'), + attr_value: str = Option('martinkuba@gmail.com', '-v', '--attributeValue', help='short name of VO'), + attr_names: str = typer.Option('urn:perun:facility:attribute-def:def:administratorContact', '-r', '--returnedAttributeNames', help='names of returned attributes'), + ) -> None: + """ facilities with attributes """ + rpc = perun.cli.rpc + attr_names = attr_names.split(',') + try: + console = Console() + facilitiesWithAttributes: list[FacilityWithAttributes] \ + = rpc.facilities_manager.get_facilities_by_attribute_with_attributes(attr_name, attr_value, attr_names) + table = Table(title="facilities") + table.add_column("id", justify="right") + table.add_column("name") + table.add_column("description") + for fwa in facilitiesWithAttributes: + table.add_row(str(fwa.facility.id), fwa.facility.name, fwa.facility.description) + console.print(table) + + for fwa in facilitiesWithAttributes: + facility_attributes: list[Attribute] = fwa.attributes + table = Table(title="facility " + fwa.facility.name + " attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in facility_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + + # does not work :-( + + + except ApiException as ex: + print('error name:', PerunException(ex).name) + print('error message:', PerunException(ex).message) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun/cli/getRichMember.py b/perun-cli-python/perun/cli/getRichMember.py index b36cdd91cf..1358674cc7 100644 --- a/perun-cli-python/perun/cli/getRichMember.py +++ b/perun-cli-python/perun/cli/getRichMember.py @@ -6,16 +6,24 @@ import perun.cli import typer +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.member import Member +from perun_openapi.model.rich_member import RichMember +from perun_openapi.model.user import User +from perun_openapi.model.vo import Vo + def get_rich_member(user_id: int = typer.Option(3197, '-u', '--user_id', help='user ID'), vo_name: str = typer.Option("meta", '-v', '--voShortName', help='short name of VO')) -> None: """ tests getting complex object of RichMember """ rpc = perun.cli.rpc try: - user = rpc.users_manager.get_user_by_id(user_id) - vo = rpc.vos_manager.get_vo_by_short_name(vo_name) - member = rpc.members_manager.get_member_by_user(vo=vo.id, user=user.id) - rich_member = rpc.members_manager.get_rich_member(member.id) + user: User = rpc.users_manager.get_user_by_id(user_id) + vo: Vo = rpc.vos_manager.get_vo_by_short_name(vo_name) + member: Member = rpc.members_manager.get_member_by_user(vo=vo.id, user=user.id) + rich_member: RichMember = rpc.members_manager.get_rich_member_with_attributes(member.id) + user_attributes: list[Attribute] = rich_member.user_attributes + member_attributes: list[Attribute] = rich_member.member_attributes console = Console() # print user table = Table(title="user") @@ -43,6 +51,26 @@ def get_rich_member(user_id: int = typer.Option(3197, '-u', '--user_id', help='u for ues in rich_member.user_ext_sources: table.add_row(str(ues.id), ues.last_access, ues.login, ues.ext_source.name) console.print(table) + # print member attributes + if member_attributes: + table = Table(title="member attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in member_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + # print user attributes + if user_attributes: + table = Table(title="user attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in user_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) except ApiException as ex: print('error name:', PerunException(ex).name) print('error message:', PerunException(ex).message) diff --git a/perun-cli-python/perun/cli/getUserAttributes.py b/perun-cli-python/perun/cli/getUserAttributes.py new file mode 100644 index 0000000000..eb918480f9 --- /dev/null +++ b/perun-cli-python/perun/cli/getUserAttributes.py @@ -0,0 +1,40 @@ +from perun_openapi import ApiException +from rich import print +from rich.console import Console +from rich.table import Table +from perun.rpc import PerunException +import typer +import perun.cli +from perun_openapi.model.attribute import Attribute +from perun_openapi.model.rich_user import RichUser +from perun_openapi.model.user import User + + +def main(user_id: int = typer.Option(3197, '-u', '--user_id', help='user ID')) -> None: + """ prints user for a given id""" + try: + console: Console = Console() + # print user + user: User = perun.cli.rpc.users_manager.get_user_by_id(user_id) + table: Table = Table(title="user") + table.add_column("id", justify="right") + table.add_column("first_name") + table.add_column("last_name") + table.add_column("createdAt") + table.add_row(str(user.id), user.first_name, user.last_name, str(user.createdAt)) + console.print(table) + # print user attributes + user_attributes: list[Attribute] = perun.cli.rpc.attributes_manager.get_user_attributes(user_id) + if user_attributes: + table = Table(title="user attributes") + table.add_column("namespace") + table.add_column("friendlyName") + table.add_column("value") + table.add_column("type") + for a in user_attributes: + table.add_row(a['namespace'], a['friendlyName'], str(a['value']), a['type']) + console.print(table) + + except ApiException as ex: + print('error:', PerunException(ex).name) + raise typer.Exit(code=1) diff --git a/perun-cli-python/perun_cli.py b/perun-cli-python/perun_cli.py index 4a8ef55f22..864202db68 100755 --- a/perun-cli-python/perun_cli.py +++ b/perun-cli-python/perun_cli.py @@ -3,11 +3,14 @@ from perun_openapi.configuration import Configuration from perun.oidc import DeviceCodeOAuth, PerunInstance from perun.rpc import PerunRpc +import perun.cli.getFacilitiesByAttribute +import perun.cli.getFacilitiesByAttributeWA import perun.cli.getGroupMembers import perun.cli.getPerunPrincipal import perun.cli.getPerunStatus import perun.cli.getRichMember import perun.cli.getUser +import perun.cli.getUserAttributes import perun.cli.listOfMyVos import perun.cli.listOfUserRoles import perun.cli.listOfVos @@ -15,11 +18,14 @@ # see https://typer.tiangolo.com/tutorial/ app = typer.Typer(add_completion=False) +app.command(name="getFacilitiesByAttribute")(perun.cli.getFacilitiesByAttribute.main) +app.command(name="getFacilitiesByAttributeWA")(perun.cli.getFacilitiesByAttributeWA.main) app.command(name="getGroupMembers")(perun.cli.getGroupMembers.get_group_members) app.command(name="getPerunPrincipal")(perun.cli.getPerunPrincipal.main) app.command(name="getPerunStatus")(perun.cli.getPerunStatus.main) app.command(name="getRichMember")(perun.cli.getRichMember.get_rich_member) app.command(name="getUser")(perun.cli.getUser.get_user) +app.command(name="getUserAttributes")(perun.cli.getUserAttributes.main) app.command(name="listOfMyVos")(perun.cli.listOfMyVos.main) app.command(name="listOfUserRoles")(perun.cli.listOfUserRoles.main) app.command(name="listOfVos")(perun.cli.listOfVos.main) diff --git a/perun-cli/Perun/beans/Application.pm b/perun-cli/Perun/beans/Application.pm index 6e80a8ffe5..1edf8371fe 100644 --- a/perun-cli/Perun/beans/Application.pm +++ b/perun-cli/Perun/beans/Application.pm @@ -66,7 +66,14 @@ sub TO_JSON $type = undef; } - return { id => $id, vo => $vo, group => $group, user => $user, type => $type, state => $state }; + my $autoApproveError; + if (defined($self->{_autoApproveError})) { + $autoApproveError = $self->{_autoApproveError}; + } else { + $autoApproveError = undef; + } + + return { id => $id, vo => $vo, group => $group, user => $user, type => $type, state => $state, autoApproveError => $autoApproveError }; } sub getId @@ -111,6 +118,12 @@ sub getState return $self->{_state}; } +sub getAutoApproveError +{ + my $self = shift; + + return $self->{_autoApproveError}; +} sub getActor { @@ -145,11 +158,12 @@ sub getCommonArrayRepresentation { my $self = shift; return ($self->{_id}, $self->{_type}, $self->{_state}, $self->{_vo}->{id} . " / " . $self->{_vo}->{shortName}, ((defined $self->{_group}) ? $self->{_group}->{id} . " / " . $self->{_group}->{name} : ""), - ((defined $self->{_user}) ? $self->getUserDisplayName : $self->{_createdBy} . " / " . $self->{_extSourceName})); + ((defined $self->{_user}) ? $self->getUserDisplayName : $self->{_createdBy} . " / " . $self->{_extSourceName}), + ((defined $self->{_autoApproveError}) ? $self->{_autoApproveError} : "")); } sub getCommonArrayRepresentationHeading { - return ('ID', 'Type', 'State', 'Vo', 'Group', 'Submitted by'); + return ('ID', 'Type', 'State', 'Vo', 'Group', 'Submitted by', 'Automatic approval error'); } 1; diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java index 95d7e09607..c4beee623f 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java @@ -6,6 +6,7 @@ import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.GroupResourceMismatchException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; @@ -30,6 +31,7 @@ import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.BanAlreadyExistsException; @@ -1538,4 +1540,43 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @return List RichMembers with specified IDs and attributes */ List getRichMembersByIds(PerunSession sess, List ids, List attrsNames) throws PrivilegeException, AttributeNotExistsException; + + /** + * Add member candidates. + * + * @param sess Perun session + * @param vo vo + * @param candidates List list of member candidates + * @throws PrivilegeException insufficient permissions + * @throws GroupNotExistsException group does not exist + * @throws UserNotExistsException user does not exist + * @throws WrongAttributeValueException attribute value is illegal + * @throws AlreadyMemberException candidate is already member + * @throws WrongReferenceAttributeValueException attribute value is illegal + * @throws ExtendMembershipException reason why user can't extend membership + * @throws VoNotExistsException vo does not exist + */ + void addMemberCandidates(PerunSession sess, Vo vo, List candidates) throws PrivilegeException, GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException; + + /** + * Add member candidates to Group. + * + * @param sess Perun session + * @param vo vo + * @param candidates List list of member candidates + * @param group group + * @throws PrivilegeException insufficient permissions + * @throws ExternallyManagedException group is externally managed + * @throws MemberNotExistsException member does not exist + * @throws GroupNotExistsException group does not exist + * @throws WrongReferenceAttributeValueException attribute value is illegal + * @throws WrongAttributeAssignmentException attribute does not belong to appropriate entity + * @throws AttributeNotExistsException attribute does not exist + * @throws AlreadyMemberException candidate is already member + * @throws WrongAttributeValueException attribute value is illegal + * @throws UserNotExistsException user does not exist + * @throws ExtendMembershipException reason why user can't extend membership + * @throws VoNotExistsException vo does not exist + */ + void addMemberCandidates(PerunSession sess, Vo vo, List candidates, Group group) throws PrivilegeException, ExternallyManagedException, MemberNotExistsException, GroupNotExistsException, WrongReferenceAttributeValueException, WrongAttributeAssignmentException, AttributeNotExistsException, AlreadyMemberException, WrongAttributeValueException, UserNotExistsException, ExtendMembershipException, VoNotExistsException; } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java index 25bbfbbc68..5cc12d4263 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java @@ -7814,6 +7814,8 @@ protected void initialize() { policies = new ArrayList<>(); policies.add(Triple.of(Role.VOADMIN, READ, RoleObject.Vo)); policies.add(Triple.of(Role.VOADMIN, WRITE, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPADMIN, READ, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPMEMBERSHIPMANAGER, READ, RoleObject.Vo)); attributes.put(attr, createInitialPolicyCollections(policies)); //urn:perun:group:attribute-def:def:groupStructureResources diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index 5024e8d11d..22ba8a9b0e 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -4,6 +4,7 @@ import cz.metacentrum.perun.core.api.AttributeDefinition; import cz.metacentrum.perun.core.api.AuthzResolver; import cz.metacentrum.perun.core.api.Candidate; +import cz.metacentrum.perun.core.api.MemberCandidate; import cz.metacentrum.perun.core.api.NamespaceRules; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.MembersPageQuery; @@ -11,6 +12,7 @@ import cz.metacentrum.perun.core.api.ExtSource; import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MemberWithSponsors; import cz.metacentrum.perun.core.api.MembersManager; import cz.metacentrum.perun.core.api.PerunSession; @@ -29,8 +31,10 @@ import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.GroupResourceMismatchException; +import cz.metacentrum.perun.core.api.exceptions.IllegalArgumentException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.api.exceptions.InvalidLoginException; import cz.metacentrum.perun.core.api.exceptions.InvalidSponsoredUserDataException; @@ -1648,6 +1652,10 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag } } + if (MembersOrderColumn.GROUP_STATUS.equals(query.getSortColumn()) && query.getGroupId() == null) { + throw new IllegalArgumentException("Group status cannot be used to sort VO members."); + } + Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames); if (query.getGroupId() == null) { @@ -1729,6 +1737,48 @@ public List getRichMembersByIds(PerunSession sess, List ids return membersManagerBl.filterOnlyAllowedAttributes(sess, richMembers); } + @Override + public void addMemberCandidates(PerunSession sess, Vo vo, List candidates) throws PrivilegeException, GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException { + Utils.checkPerunSession(sess); + + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "addMemberCandidates_Vo_List_policy", vo)) { + throw new PrivilegeException(sess, "addMemberCandidates"); + } + + for (MemberCandidate candidate : candidates) { + if (candidate.getRichUser() != null) { + Member member = this.createMember(sess, vo, candidate.getRichUser()); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } else if (candidate.getCandidate() != null) { + Member member = this.createMember(sess, vo, candidate.getCandidate()); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } + } + } + + @Override + public void addMemberCandidates(PerunSession sess, Vo vo, List candidates, Group group) throws PrivilegeException, ExternallyManagedException, MemberNotExistsException, GroupNotExistsException, WrongReferenceAttributeValueException, WrongAttributeAssignmentException, AttributeNotExistsException, AlreadyMemberException, WrongAttributeValueException, UserNotExistsException, ExtendMembershipException, VoNotExistsException { + Utils.checkPerunSession(sess); + + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "addMemberCandidates_Vo_List_Group_policy", group)) { + throw new PrivilegeException(sess, "addMemberCandidates"); + } + + for (MemberCandidate candidate : candidates) { + if (candidate.getMember() != null) { + getPerunBl().getGroupsManager().addMember(sess, group, candidate.getMember()); + } else if (candidate.getRichUser() != null) { + Member member = this.createMember(sess, vo, candidate.getRichUser(), List.of(group)); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } else if (candidate.getCandidate() != null) { + Member member = this.createMember(sess, vo, candidate.getCandidate(), List.of(group)); + getPerunBl().getMembersManagerBl().validateMemberAsync(sess, member); + } + } + } + /** * Converts member to member with sponsors and sets all his sponsors. * diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java index e66783e94b..81632502e9 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 @@ -854,11 +854,14 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { String voSelect = "SELECT " + memberMappingSelectQuery + " ,count(*) OVER() AS total_count" + - " FROM members JOIN users on members.user_id = users.id"; + query.getSortColumn().getSqlSelect() + + " FROM members JOIN users on members.user_id = users.id " + + query.getSortColumn().getSqlJoin(); String groupSelect = "SELECT " + groupsMembersMappingSelectQuery + " ,count(*) OVER() AS total_count" + + query.getSortColumn().getSqlSelect() + " FROM" + " (SELECT group_id, member_id, min(source_group_status) as source_group_status," + " min(membership_type) as membership_type, null as source_group_id" + @@ -866,7 +869,8 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { " WHERE group_id = (:groupId)" + " GROUP BY group_id, member_id) groups_members" + " LEFT JOIN members on groups_members.member_id = members.id" + - " LEFT JOIN users on members.user_id = users.id"; + " LEFT JOIN users on members.user_id = users.id " + + query.getSortColumn().getSqlJoin(); return query.getGroupId() == null ? voSelect : groupSelect; } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java deleted file mode 100644 index d62aeee66c..0000000000 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.core.impl.modules.attributes; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributeDefinition; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.ExtSourcesManager; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.impl.PerunSessionImpl; -import cz.metacentrum.perun.core.impl.Utils; -import cz.metacentrum.perun.core.implApi.modules.attributes.SkipValueCheckDuringDependencyCheck; -import cz.metacentrum.perun.core.implApi.modules.attributes.UserVirtualAttributesModuleAbstract; - -import java.util.Collections; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Attribute value depends on login-namespace:mu attribute - * Deprecated, use urn_perun_user_attribute_def_virt_optional_login_namespace_mu instead. - * - * @author Simona Kruppova, Michal Stava - */ -@Deprecated -@SkipValueCheckDuringDependencyCheck -public class urn_perun_user_attribute_def_virt_optionalLogin_namespace_mu extends UserVirtualAttributesModuleAbstract { - - private final String EXTSOURCE_MUNI_IDP2 = "https://idp2.ics.muni.cz/idp/shibboleth"; - private static final Pattern loginMUPattern = Pattern.compile("^([0-9]+)[@]muni[.]cz$"); - - private static final String A_U_D_loginNamespace_mu = AttributesManager.NS_USER_ATTR_DEF + ":login-namespace:mu"; - - @Override - public Attribute getAttributeValue(PerunSessionImpl sess, User user, AttributeDefinition attributeDefinition) { - Attribute attribute = new Attribute(attributeDefinition); - - try { - Attribute loginInMU = sess.getPerunBl().getAttributesManagerBl().getAttribute(sess, user, A_U_D_loginNamespace_mu); - Utils.copyAttributeToVirtualAttributeWithValue(loginInMU, attribute); - } catch (AttributeNotExistsException ex) { - //That means that mu login attribute not exists at all - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } - - //if attribute is still null (empty login in mu or not existing attribute), try to find uco in user ext sources - if(attribute.getValue() == null) { - List userExtSources = sess.getPerunBl().getUsersManagerBl().getUserExtSources(sess, user); - for(UserExtSource userExtSource : userExtSources) { - ExtSource extSource = userExtSource.getExtSource(); - - //Skip if extSource is not the one we are looking for - if(userExtSource.getLogin() == null || extSource == null) continue; - if(!ExtSourcesManager.EXTSOURCE_IDP.equals(extSource.getType())) continue; - if(!EXTSOURCE_MUNI_IDP2.equals(extSource.getName())) continue; - - //Get login from this extSource and get only UCO from it - String login = userExtSource.getLogin(); - Matcher loginMUMatcher = loginMUPattern.matcher(login); - //This user has login in mu, but in weird format so skip this one - if(!loginMUMatcher.find()) continue; - //It is ok, take UCO from login and set it to attribute value - String UCO = loginMUMatcher.group(1); - attribute.setValue(UCO); - break; - } - } - - return attribute; - } - - @Override - public List getStrongDependencies() { - return Collections.singletonList(A_U_D_loginNamespace_mu); - } - - @Override - public AttributeDefinition getAttributeDefinition() { - AttributeDefinition attr = new AttributeDefinition(); - attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); - attr.setFriendlyName("optionalLogin-namespace:mu"); - attr.setDisplayName("MU login (if available)"); - attr.setType(String.class.getName()); - attr.setDescription("Masaryk University login (if available)"); - return attr; - } -} diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 4bd923f365..60d8391721 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -6,6 +6,12 @@ -- Directly under version number should be version commands. They will be executed in the order they are written here. -- Comments are prefixed with -- and can be written only between version blocks, that means not in the lines with commands. They have to be at the start of the line. +3.2.11 +ALTER TABLE vos DROP constraint vo_u; +ALTER TABLE vos ADD CONSTRAINT vo_u unique (short_name); +ALTER TABLE application ADD COLUMN auto_approve_error varchar; +UPDATE configurations set value='3.2.11' WHERE property='DATABASE VERSION'; + 3.2.10 ALTER TABLE resources_bans ALTER COLUMN banned_to SET DEFAULT '2999-01-01 00:00:00'; ALTER TABLE facilities_bans ALTER COLUMN banned_to SET DEFAULT '2999-01-01 00:00:00'; diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java index dd9a03bb47..c2ff1dff3e 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java @@ -14,12 +14,14 @@ import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.GroupsManager; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.MemberCandidate; import cz.metacentrum.perun.core.api.MemberGroupStatus; import cz.metacentrum.perun.core.api.MemberWithSponsors; import cz.metacentrum.perun.core.api.MembersManager; import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.NamespaceRules; +import cz.metacentrum.perun.core.api.RichUser; import cz.metacentrum.perun.core.api.SortingOrder; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.PerunBean; @@ -42,15 +44,19 @@ import cz.metacentrum.perun.core.api.exceptions.AlreadySponsorException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsoredMemberException; import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; import cz.metacentrum.perun.core.api.exceptions.MemberLifecycleAlteringForbiddenException; import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParseUserNameException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.modules.attributes.AbstractMembershipExpirationRulesModule; import cz.metacentrum.perun.core.api.SponsoredUserData; @@ -70,7 +76,6 @@ import java.util.Map; import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS; -import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS_HISTORY; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.VO_EXPIRATION_RULES_ATTR; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.expireSponsoredMembers; import static java.util.stream.Collectors.toList; @@ -250,6 +255,34 @@ public void getAllSponsoredMembersAndTheirSponsors() throws Exception { assertEquals(1, memberWithSponsors.get(0).getSponsors().size()); } + @Test + public void addMemberCandidates() throws Exception { + System.out.println(CLASS_NAME + "addMemberCandidates"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(123, "test", "test")); + + MemberCandidate memberCandidate1 = getMemberCandidateWithCandidate(); + MemberCandidate memberCandidate2 = getMemberCandidateWithRichUser(); + + perun.getMembersManager().addMemberCandidates(sess, vo, List.of(memberCandidate1, memberCandidate2)); + assertEquals(2, perun.getMembersManager().getMembers(sess, vo).size()); + } + + @Test + public void addMemberCandidatesGroup() throws Exception { + System.out.println(CLASS_NAME + "addMemberCandidatesGroup"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(123, "test", "test")); + Group group = perun.getGroupsManager().createGroup(sess, vo, new Group("test", "test")); + + MemberCandidate memberCandidate1 = getMemberCandidateWithCandidate(); + MemberCandidate memberCandidate2 = getMemberCandidateWithRichUser(); + MemberCandidate memberCandidate3 = getMemberCandidateWithMember(vo); + + perun.getMembersManager().addMemberCandidates(sess, vo, List.of(memberCandidate1, memberCandidate2, memberCandidate3), group); + assertEquals(3, perun.getMembersManager().getMembers(sess, vo).size()); + } + @Test public void createMember() throws Exception { System.out.println(CLASS_NAME + "createMemberSync"); @@ -2846,6 +2879,126 @@ public void getMembersPageIdSortWorks() throws Exception { .containsExactly(member1.getId(), member2.getId(), member3.getId()); } + @Test + public void getMembersPageSortsByEmail() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByEmail"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + AttributeDefinition mailAttrDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, A_M_MAIL); + AttributeDefinition prefMailAttrDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, A_U_PREFERRED_MAIL); + + Attribute memberMail = new Attribute(mailAttrDef); + Attribute prefMail = new Attribute(prefMailAttrDef); + + Member member1 = setUpMember(vo, "Doe", "John"); + memberMail.setValue("mail@mail.com"); + perun.getAttributesManagerBl().setAttribute(sess, member1, memberMail); + + Member member2 = setUpMember(vo, "Do", "Jo"); + memberMail.setValue("nail@nail.com"); + perun.getAttributesManagerBl().setAttribute(sess, member2, memberMail); + + Member member3 = setUpMember(vo, "Nom Ail", "Madelin"); + + Member member4 = setUpMember(vo, "Don", "Joe"); + prefMail.setValue("ali@k.com"); + memberMail.setValue("zli@k.com"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member4), prefMail); + perun.getAttributesManagerBl().setAttribute(sess, member4, memberMail); + + Member member5 = setUpMember(vo, "Doney", "Joey"); + prefMail.setValue("Don@ey.com"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member5), prefMail); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.EMAIL); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of(prefMail.getName(), memberMail.getName())); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) // alik, doney, mail, nail, NULL + .containsExactly(member4.getId(), member5.getId(), member1.getId(), member2.getId(), member3.getId()); + } + + @Test + public void getMembersPageSortsByOrganization() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByOrganization"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + AttributeDefinition memberOrgDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, AttributesManager.NS_MEMBER_ATTR_DEF + ":organization"); + AttributeDefinition userOrgDef = perun.getAttributesManagerBl().getAttributeDefinition(sess, AttributesManager.NS_USER_ATTR_DEF + ":organization"); + + Attribute memberOrg = new Attribute(memberOrgDef); + Attribute userOrg = new Attribute(userOrgDef); + + Member member3 = setUpMember(vo, "Don", "Joe"); + userOrg.setValue("eleland"); + memberOrg.setValue("Zazaland"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member3), userOrg); + perun.getAttributesManagerBl().setAttribute(sess, member3, memberOrg); + + Member member1 = setUpMember(vo, "Doe", "John"); + memberOrg.setValue("Babaland"); + perun.getAttributesManagerBl().setAttribute(sess, member1, memberOrg); + + Member member4 = setUpMember(vo, "Doney", "Joey"); + userOrg.setValue("gagaland"); + perun.getAttributesManagerBl().setAttribute(sess, perun.getUsersManagerBl().getUserByMember(sess, member4), userOrg); + + Member member5 = setUpMember(vo, "Nom Ail", "Madelin"); + + Member member2 = setUpMember(vo, "Do", "Jo"); + memberOrg.setValue("Dadaland"); + perun.getAttributesManagerBl().setAttribute(sess, member2, memberOrg); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.ORGANIZATION); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of(userOrg.getName(), memberOrg.getName())); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) + .containsExactly(member1.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageSortsByStatus() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageSortsByStatus"); + + Vo vo = perun.getVosManager().createVo(sess, new Vo(0, "testPagination", "tp")); + + Member member1 = setUpMember(vo, "Doe", "John"); + perun.getMembersManagerBl().validateMember(sess, member1); + perun.getMembersManagerBl().setStatus(sess, member1, Status.getStatus(4)); + + Member member2 = setUpMember(vo, "Do", "Jo"); + perun.getMembersManagerBl().validateMember(sess, member2); + perun.getMembersManagerBl().setStatus(sess, member2, Status.getStatus(3)); + + Member member3 = setUpMember(vo, "Do", "Jo"); + perun.getMembersManagerBl().setStatus(sess, member3, Status.getStatus(0)); + + Member member4 = setUpMember(vo, "Don", "Joe"); + perun.getMembersManagerBl().setStatus(sess, member4, Status.getStatus(1)); + + MembersPageQuery query = new MembersPageQuery(5, 0, SortingOrder.ASCENDING, MembersOrderColumn.STATUS); + + Paginated result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of()); + + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + assertThat(returnedMemberIds) + .containsExactly(member3.getId(), member4.getId(), member2.getId(), member1.getId()); + } + @Test public void getMembersPageIdSortDescendingWorks() throws Exception { System.out.println(CLASS_NAME + "getMembersPageIdSortDescendingWorks"); @@ -3126,6 +3279,12 @@ public void getGroupMembersPageOnDifferentSubgroupStatuses() throws Exception { assertThat(returnedMemberIds).containsExactly(member2.getId()); assertThat(result.getData().get(0).getMembershipType()).isEqualTo(MembershipType.DIRECT); + + query.setSortColumn(MembersOrderColumn.GROUP_STATUS); + query.setGroupStatuses(List.of()); + result = perun.getMembersManager().getMembersPage(sess, vo, query, List.of()); + + assertThat(result.getData().get(2).getId()).isEqualTo(member2.getId()); } @Test @@ -3605,4 +3764,49 @@ private Member createSponsoredMember(PerunSession sess, Vo vo, String namespace, return perun.getMembersManagerBl().createSponsoredMember(sess, input, vo, sponsor, null, false, null,null, Validation.SYNC); } + + private MemberCandidate getMemberCandidateWithCandidate() { + Candidate candidate = new Candidate(); + candidate.setFirstName("Test"); + candidate.setId(0); + candidate.setMiddleName(""); + candidate.setLastName("Test"); + candidate.setTitleBefore(""); + candidate.setTitleAfter(""); + candidate.setUserExtSource(new UserExtSource(new ExtSource(0, "testExtSource", ExtSourcesManager.EXTSOURCE_INTERNAL), "Test1")); + candidate.setAttributes(new HashMap<>()); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setCandidate(candidate); + + return memberCandidate; + } + + private MemberCandidate getMemberCandidateWithRichUser() { + User user = new User(); + user.setFirstName("Test2"); + user.setLastName("Test2"); + user = perun.getUsersManagerBl().createUser(sess, user); + + RichUser richUser = new RichUser(user, List.of(new UserExtSource(new ExtSource(1, "test2ExtSource", ExtSourcesManager.EXTSOURCE_INTERNAL), "Test2"))); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setRichUser(richUser); + + return memberCandidate; + } + + private MemberCandidate getMemberCandidateWithMember(Vo vo) throws GroupNotExistsException, UserNotExistsException, WrongReferenceAttributeValueException, PrivilegeException, AlreadyMemberException, WrongAttributeValueException, ExtendMembershipException, VoNotExistsException { + User userForMember = new User(); + userForMember.setFirstName("Test3"); + userForMember.setLastName("Test3"); + userForMember = perun.getUsersManagerBl().createUser(sess, userForMember); + + Member member = perun.getMembersManager().createMember(sess, vo, userForMember); + + MemberCandidate memberCandidate = new MemberCandidate(); + memberCandidate.setMember(member); + + return memberCandidate; + } } diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index 2ec9748b13..ecec653d05 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.10 (don't forget to update insert statement at the end of file) +-- database version 3.2.11 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -12,7 +12,7 @@ create table vos ( created_by_uid integer, modified_by_uid integer, constraint vo_pk primary key (id), - constraint vo_u unique (name) + constraint vo_u unique (short_name) ); -- USERS - information about user as real person @@ -515,6 +515,7 @@ create table application ( state varchar, --state of application (new/verified/approved/rejected) extSourceLoa integer, --level of assurance of user by external source group_id integer, --identifier of group (groups.id) if application is for group + auto_approve_error varchar, --error that occurred during automatic approval created_at timestamp default statement_timestamp() not null, created_by varchar default user not null, modified_at timestamp default statement_timestamp() not null, @@ -1988,7 +1989,7 @@ grant all on attribute_critical_actions to perun; grant all on app_notifications_sent to perun; -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.10'); +insert into configurations values ('DATABASE VERSION','3.2.11'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java index 6dd5644dd3..f9c872354d 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/PerunNotifEmailManagerImpl.java @@ -17,8 +17,6 @@ @Service("perunNotifEmailManager") public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { - @Autowired - private Properties propertiesBean; private boolean sendMessages; @@ -29,8 +27,7 @@ public class PerunNotifEmailManagerImpl implements PerunNotifEmailManager { @PostConstruct public void init() throws Exception { - String sendMessages_s = (String) propertiesBean.get("notif.sendMessages"); - this.sendMessages = sendMessages_s != null && sendMessages_s.equals("true"); + this.sendMessages = BeansUtils.getCoreConfig().getNotifSendMessages(); } @Override diff --git a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java index 41fd42e121..4a69b09264 100644 --- a/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java +++ b/perun-notification/src/main/java/cz/metacentrum/perun/notif/managers/SchedulingManagerImpl.java @@ -8,11 +8,9 @@ import cz.metacentrum.perun.notif.entities.PerunNotifPoolMessage; import cz.metacentrum.perun.notif.utils.NotifUtils; import java.util.List; -import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; -import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -31,14 +29,8 @@ public class SchedulingManagerImpl { private static final Logger logger = LoggerFactory.getLogger(SchedulingManagerImpl.class); private PerunSession session; - private static volatile AtomicBoolean running = new AtomicBoolean(false); - private static volatile AtomicBoolean runningAllowed = new AtomicBoolean(true); - - @Autowired - private Properties propertiesBean; - - @Autowired - private DataSource dataSource; + private static final AtomicBoolean running = new AtomicBoolean(false); + private static final AtomicBoolean runningAllowed = new AtomicBoolean(true); @Autowired private PerunNotifPoolMessageManager perunNotifPoolMessageManager; @@ -55,8 +47,6 @@ public class SchedulingManagerImpl { @Autowired private PerunBl perun; - private final String consumerName = "notifications"; - @PostConstruct public void init() { session = NotifUtils.getPerunSession(perun); @@ -97,12 +87,10 @@ public void doNotification() { /** * Loads notif audit messages from db restart their processing. * Call processing of one perunAuditMessage for each gotten msg. - * - * @throws InternalErrorException */ private void processPerunNotifAuditMessages() throws Exception { - List oldAuditMessages = null; + List oldAuditMessages; try { oldAuditMessages = perunNotifAuditMessagesManager.getAll(); } catch (Exception ex) { @@ -119,9 +107,9 @@ private void processPerunNotifAuditMessages() throws Exception { /** * The method loads perun audit messages from the database and saves them as PerunNotifAudiMessages. */ - public void processPerunAuditMessages() throws Exception { + public void processPerunAuditMessages() { try { - List events = perun.getAuditMessagesManagerBl().pollConsumerEvents(session, consumerName); + List events = perun.getAuditMessagesManagerBl().pollConsumerEvents(session, "notifications"); for (AuditEvent event : events) { try { perunNotifAuditMessagesManager.saveMessageToPerunAuditerMessage(event.getMessage(), session); @@ -141,10 +129,6 @@ public void processPerunAuditMessages() throws Exception { * auditer message from db. To accomplish a success, the message have to be well-formed, so that the * object can be parsed, there have to be matching notifRegex in the db. If the message is recognized and * the matching regex is assigned to the template, PerunNotifPoolMessage is created. - * - * @param perunAuditMessage - * @param session - * @throws InternalErrorException */ private void processPerunNotifAuditMessage(PerunNotifAuditMessage perunAuditMessage, PerunSession session) throws Exception { diff --git a/perun-notification/src/main/resources/perun-notification.xml b/perun-notification/src/main/resources/perun-notification.xml index 10e7a7b3bb..5298beede2 100644 --- a/perun-notification/src/main/resources/perun-notification.xml +++ b/perun-notification/src/main/resources/perun-notification.xml @@ -42,31 +42,4 @@ http://www.springframework.org/schema/context http://www.springframework.org/sch - - - - - - file:@perun.conf@perun-notification.properties - file:${perun.conf.custom}perun-notification.properties - - - - - - - - - - - - perun@localhost - perun@localhost test message - true - notifications - - - - - diff --git a/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java b/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java index 11a9900c3b..344005ebbf 100644 --- a/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java +++ b/perun-notification/src/test/java/cz/metacentrum/perun/notif/AbstractTest.java @@ -49,9 +49,6 @@ public class AbstractTest { @Autowired private ApplicationContext appContext; - @Autowired - Properties propertiesBean; - public static String convertStreamToString(java.io.InputStream is) { java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; @@ -67,8 +64,7 @@ public static void shutdown() { @Before public void startSmtpServer() { if (smtpServer == null) { - int port = (propertiesBean.getProperty("notif.port") != null) ? Integer.parseInt(propertiesBean.getProperty("notif.port")) : 8086; - smtpServer = SimpleSmtpServer.start(port); + smtpServer = SimpleSmtpServer.start(8086); } } diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 7a566d5f7b..ec920dbb29 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -227,6 +227,10 @@ components: enum: - ID - NAME + - EMAIL + - GROUP_STATUS + - STATUS + - ORGANIZATION MemberGroupStatus: type: string @@ -496,11 +500,12 @@ components: propertyName: beanName FacilityWithAttributes: - allOf: - - $ref: '#/components/schemas/Facility' + type: object properties: + facility: { $ref: '#/components/schemas/Facility' } attributes: { type: array, items: {$ref: '#/components/schemas/Attribute'} } required: + - facility - attributes Host: @@ -1073,6 +1078,7 @@ components: createdAt: { type: string } modifiedBy: { type: string } modifiedAt: { type: string } + autoApproveError: { type: string } RichApplication: allOf: @@ -2756,7 +2762,7 @@ components: required: true attrNames: - name: "attrNames[]" + name: "attrNames" description: "list of attribute names List" in: query required: true @@ -3475,6 +3481,16 @@ components: in: query required: false + memberCandidates: + name: candidates[] + description: list of member candidates + schema: + type: array + items: + $ref: "#/components/schemas/MemberCandidate" + in: query + required: true + ################################################# @@ -10919,6 +10935,33 @@ paths: attrNames: { type: array, items: { type: string } } query: { $ref: '#/components/schemas/MembersPageQuery' } + /json/membersManager/addMemberCandidates: + post: + tags: + - MembersManager + operationId: addMemberCandidates + summary: Adds member candidates. + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + requestBody: + required: true + content: + application/json: + schema: + title: InputAddMemberCandidates + description: "input to add member candidates" + type: object + required: + - vo + - candidates + properties: + vo: { type: integer } + candidates: { type: array, items: { $ref: '#/components/schemas/MemberCandidate' } } + group: { type: integer } + ################################################# # # # FacilitiesManager # @@ -16764,6 +16807,50 @@ paths: default: $ref: '#/components/responses/ExceptionResponse' + /json/registrarManager/inviteMemberCandidates: + post: + tags: + - RegistrarManager + operationId: inviteMemberCandidates + summary: Invite member candidates. + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + requestBody: + required: true + content: + application/json: + schema: + title: InputInviteMemberCandidates + description: "input to invite member candidates" + type: object + required: + - vo + - candidates + - lang + properties: + vo: { type: integer } + candidates: { type: array, items: { $ref: '#/components/schemas/MemberCandidate' } } + lang: { type: string } + group: { type: integer } + + /json/registrarManager/invitationFormExists: + get: + tags: + - RegistrarManager + operationId: invitationFormExists + summary: Checks if invitation form exists. + parameters: + - $ref: '#/components/parameters/voId' + - $ref: '#/components/parameters/optionalGroupId' + responses: + '200': + $ref: '#/components/responses/BooleanResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + ################################################# # # # ServicesManager # diff --git a/perun-openapi/pom.xml b/perun-openapi/pom.xml index 6c182925c8..f46b209e21 100644 --- a/perun-openapi/pom.xml +++ b/perun-openapi/pom.xml @@ -28,7 +28,7 @@ org.openapitools openapi-generator-maven-plugin - 6.2.1 + 6.3.0 diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java index 9888083a72..37c6e5e232 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/MailManager.java @@ -227,4 +227,14 @@ public interface MailManager { */ String getPropertyFromConfiguration(String input); + /** + * Checks if invitation form exists + * + * @param vo vo + * @param group group + * @return true if invitation form exists, false otherwise + * @throws VoNotExistsException when vo does not exist + * @throws GroupNotExistsException when group is defined and does not exist + */ + Boolean invitationFormExists(PerunSession sess, Vo vo, Group group) throws VoNotExistsException, GroupNotExistsException; } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java index 8d4908ed16..435f7917b8 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/RegistrarManager.java @@ -662,4 +662,15 @@ public interface RegistrarManager { */ void addGroupsToAutoRegistration(PerunSession sess, List groups) throws GroupNotExistsException, PrivilegeException, GroupNotAllowedToAutoRegistrationException; + /** + * Invite member candidates. + * + * @param sess session + * @param vo Vo + * @param lang language + * @param candidates list of member candidates + * @param group group + * @throws PerunException unable to invite some candidate + */ + void inviteMemberCandidates(PerunSession sess, Vo vo, Group group, String lang, List candidates) throws PerunException; } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java index d3c2211f24..e86a982b21 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/impl/MailManagerImpl.java @@ -108,6 +108,7 @@ public class MailManagerImpl implements MailManager { private static final String FIELD_ACTOR = "{actor}"; private static final String FIELD_EXT_SOURCE = "{extSource}"; private static final String FIELD_CUSTOM_MESSAGE = "{customMessage}"; + private static final String FIELD_AUTO_APPROVE_ERROR = "{autoApproveError}"; private static final String FIELD_FIRST_NAME = "{firstName}"; private static final String FIELD_LAST_NAME = "{lastName}"; private static final String FIELD_ERRORS = "{errors}"; @@ -794,6 +795,25 @@ public String getPropertyFromConfiguration(String propertyName) { return EMPTY_STRING; } + @Override + public Boolean invitationFormExists(PerunSession sess, Vo vo, Group group) throws VoNotExistsException, GroupNotExistsException { + Utils.checkPerunSession(sess); + + perun.getVosManagerBl().checkVoExists(sess, vo); + if (group != null) { + perun.getGroupsManagerBl().checkGroupExists(sess, group); + } + + try { + ApplicationForm form = getForm(vo, group); + getMail(form, AppType.INITIAL, MailType.USER_INVITE); + } catch (FormNotExistsException | RegistrarException e) { + return false; + } + + return true; + } + /** * Retrieve mail definition from db by params. * Mail contains all texts. @@ -1288,6 +1308,7 @@ private String buildInviteURL(Vo vo, Group group, String text) { * {phone} - user phone submitted on application or stored in a system * * {customMessage} - message passed by the admin to mail (e.g. reason of application reject) + * {autoApproveError} - error that caused automatic approval failure * {errors} - include errors, which occurred when processing registrar actions * (e.g. login reservation errors passed to mail for VO admin) * @@ -1343,6 +1364,15 @@ private String substituteCommonStrings(Application app, List otherApps = registrarManager.getOpenApplicationsForUserInVo(helperSess, app.getVo()); + for (Application otherApp : otherApps) { + registrarManager.rejectApplication(sess, otherApp.getId(), DEFAULT_GROUP_MSG_VO); + } + } // log perun.getAuditer().log(sess, new ApplicationRejected(app)); @@ -1574,7 +1591,7 @@ public Application rejectApplication(PerunSession sess, int appId, String reason } // send mail - getMailManager().sendMessage(app, MailType.APP_REJECTED_USER, reason, null); + getMailManager().sendMessage(app, MailType.APP_REJECTED_USER, reason,null); perun.getAuditer().log(sess, new ApplicationRejected(app)); @@ -1619,7 +1636,6 @@ private void deleteApplicationReservedLogins(PerunSession sess, Application app) @Override public Application approveApplication(PerunSession sess, int appId) throws PerunException { - synchronized(runningApproveApplication) { if (runningApproveApplication.contains(appId)) { throw new AlreadyProcessingException("Application approval is already processing."); @@ -3597,6 +3613,19 @@ public void addGroupsToAutoRegistration(PerunSession sess, List groups) t perun.getGroupsManagerBl().addGroupsToAutoRegistration(sess, groups); } + @Override + public void inviteMemberCandidates(PerunSession sess, Vo vo, Group group, String lang, List candidates) throws PerunException { + Utils.checkPerunSession(sess); + + for (MemberCandidate candidate : candidates) { + if (candidate.getRichUser() != null) { + mailManager.sendInvitation(sess, vo, group, perun.getUsersManager().getUserById(sess, candidate.getRichUser().getId())); + } else if (candidate.getCandidate() != null) { + mailManager.sendInvitation(sess, vo, group, null, getCandidateEmail(candidate.getCandidate()), lang); + } + } + } + @Override public void handleUsersGroupApplications(PerunSession sess, Vo vo, User user) throws PerunException { // get group apps based on the vo @@ -3654,6 +3683,21 @@ public ConsolidatorManager getConsolidatorManager() { return this.consolidatorManager; } + /** + * Gets email for candidate. + * + * @param candidate candidate + * @return email + */ + private String getCandidateEmail(Candidate candidate) { + if (candidate.getAttributes().containsKey("urn:perun:member:attribute-def:def:mail")) { + return candidate.getAttributes().get("urn:perun:member:attribute-def:def:mail"); + } else if (candidate.getAttributes().containsKey("urn:perun:user:attribute-def:def:preferredMail")) { + return candidate.getAttributes().get("urn:perun:user:attribute-def:def:preferredMail"); + } + return ""; + } + /** * Set application to VERIFIED state if all it's * mails (VALIDATED_EMAIL) have assuranceLevel >= 1 and have non-empty value (there is anything to validate). @@ -3731,13 +3775,13 @@ private void setApplicationState(PerunSession sess, int appId, AppState appState /** * Try to approve application if auto-approve is possible * - * @param sess user who try to approves application - * @param app application to approve + * @param sess user who tries to approve application + * @param app application to be approved * @throws InternalErrorException */ private void tryToAutoApproveApplication(PerunSession sess, Application app) throws PerunException { - ApplicationForm form; + if (app.getGroup() != null) { // group application form = getFormForGroup(app.getGroup()); @@ -3745,15 +3789,15 @@ private void tryToAutoApproveApplication(PerunSession sess, Application app) thr // vo application form = getFormForVo(app.getVo()); } + AppType type = app.getType(); - if (AppType.INITIAL.equals(type) && !form.isAutomaticApproval()) return; - if (AppType.EXTENSION.equals(type) && !form.isAutomaticApprovalExtension()) return; - if (AppType.EMBEDDED.equals(type) && !form.isAutomaticApprovalEmbedded()) return; + if ((AppType.INITIAL.equals(type) && !form.isAutomaticApproval()) || (AppType.EXTENSION.equals(type) && !form.isAutomaticApprovalExtension()) || (AppType.EMBEDDED.equals(type) && !form.isAutomaticApprovalEmbedded())) { + return; + } // do not auto-approve Group applications, if user is not member of VO if (app.getGroup() != null && app.getVo() != null) { - try { if (app.getUser() == null) { LinkedHashMap additionalAttributes = BeansUtils.stringToMapOfAttributes(app.getFedInfo()); @@ -3763,19 +3807,15 @@ private void tryToAutoApproveApplication(PerunSession sess, Application app) thr membersManager.getMemberByUser(sess, app.getVo(), u); } else { // user not found or null, hence can't be member of VO -> do not approve. + setAutoApproveErrorToApplication(app, "This application is waiting for approval of the VO application, which must be approved by the VO manager first. After that, this application will be automatically approved."); return; } } else { // user known, but maybe not member of a vo membersManager.getMemberByUser(sess, app.getVo(), app.getUser()); } - } catch (MemberNotExistsException ex) { - return; - } catch (UserNotExistsException ex) { - return; - } catch (UserExtSourceNotExistsException ex) { - return; - } catch (ExtSourceNotExistsException ex) { + } catch (MemberNotExistsException | UserNotExistsException | UserExtSourceNotExistsException | ExtSourceNotExistsException ex) { + setAutoApproveErrorToApplication(app, "This application is waiting for approval of the VO application, which must be approved by the VO manager first. After that, this application will be automatically approved."); return; } } @@ -3809,14 +3849,10 @@ private void tryToAutoApproveApplication(PerunSession sess, Application app) thr } } catch (Exception ex) { - - ArrayList list = new ArrayList<>(); - list.add(ex); - getMailManager().sendMessage(app, MailType.APP_ERROR_VO_ADMIN, null, list); - + setAutoApproveErrorToApplication(app, ex.getMessage()); + getMailManager().sendMessage(app, MailType.APP_ERROR_VO_ADMIN, null, List.of(ex)); throw ex; } - } /** @@ -4758,6 +4794,16 @@ private void fixDependencies(ApplicationForm form, Map idsTran } } + /** + * Sets error that occurred during automatic approval of application. + * + * @param application application + * @param error error + */ + private void setAutoApproveErrorToApplication(Application application, String error) { + jdbc.update("UPDATE application SET auto_approve_error=? WHERE id=?", error, application.getId()); + } + /** * Returns number of embedded groups form items. */ @@ -4897,15 +4943,15 @@ private static ResultSetExtractor> getPaginatedApplic } // FIXME - we are retrieving GROUP name using only "short_name" so it's not same as getGroupById() - static final String APP_SELECT = "select a.id as id,a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + - "a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + + static final String APP_SELECT = "select a.id as id, a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + + "a.user_id as user_id, a.auto_approve_error as auto_approve_error, a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + "v.name as vo_name, v.short_name as vo_short_name, v.created_by as vo_created_by, v.created_at as vo_created_at, v.created_by_uid as vo_created_by_uid, v.modified_by as vo_modified_by, " + "v.modified_at as vo_modified_at, v.modified_by_uid as vo_modified_by_uid, g.name as group_name, g.dsc as group_description, g.created_by as group_created_by, g.created_at as group_created_at, g.modified_by as group_modified_by, g.created_by_uid as group_created_by_uid, g.modified_by_uid as group_modified_by_uid," + "g.modified_at as group_modified_at, g.vo_id as group_vo_id, g.parent_group_id as group_parent_group_id, g.uu_id as group_uu_id, u.first_name as user_first_name, u.last_name as user_last_name, u.middle_name as user_middle_name, " + "u.title_before as user_title_before, u.title_after as user_title_after, u.service_acc as user_service_acc, u.sponsored_acc as user_sponsored_acc , u.uu_id as user_uu_id from application a left outer join vos v on a.vo_id = v.id left outer join groups g on a.group_id = g.id left outer join users u on a.user_id = u.id"; - static final String APP_SELECT_PAGE = "select a.id as id,a.vo_id as vo_id, a.group_id as group_id,a.apptype as apptype,a.fed_info as fed_info,a.state as state," + - "a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + + static final String APP_SELECT_PAGE = "select a.id as id, a.vo_id as vo_id, a.group_id as group_id, a.apptype as apptype, a.fed_info as fed_info,a.state as state," + + "a.auto_approve_error as auto_approve_error, a.user_id as user_id,a.extsourcename as extsourcename, a.extsourcetype as extsourcetype, a.extsourceloa as extsourceloa, a.user_id as user_id, a.created_at as app_created_at, a.created_by as app_created_by, a.modified_at as app_modified_at, a.modified_by as app_modified_by, " + "v.name as vo_name, v.short_name as vo_short_name, v.created_by as vo_created_by, v.created_at as vo_created_at, v.created_by_uid as vo_created_by_uid, v.modified_by as vo_modified_by, " + "v.modified_at as vo_modified_at, v.modified_by_uid as vo_modified_by_uid, g.name as group_name, g.dsc as group_description, g.created_by as group_created_by, g.created_at as group_created_at, g.modified_by as group_modified_by, g.created_by_uid as group_created_by_uid, g.modified_by_uid as group_modified_by_uid," + "g.modified_at as group_modified_at, g.vo_id as group_vo_id, g.parent_group_id as group_parent_group_id, g.uu_id as group_uu_id, u.first_name as user_first_name, u.last_name as user_last_name, u.middle_name as user_middle_name, " + @@ -4975,6 +5021,7 @@ private static ResultSetExtractor> getPaginatedApplic app.setCreatedBy(resultSet.getString("app_created_by")); app.setModifiedAt(resultSet.getString("app_modified_at")); app.setModifiedBy(resultSet.getString("app_modified_by")); + app.setAutoApproveError(resultSet.getString("auto_approve_error")); return app; diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java deleted file mode 100644 index 84ae2b815d..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CvutFbmi.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.Group; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * This module sorts new VO members into the groups based on their affiliations from ČVUT/UK IdPs - * - * @author Pavel Zlámal - */ -public class CvutFbmi extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(CvutFbmi.class); - - /** - * Add approved VO members into specific groups based on their affiliation from ČVUT or UK IdP - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws MemberNotExistsException, WrongAttributeAssignmentException, AttributeNotExistsException, GroupNotExistsException { - - PerunBl perun = (PerunBl)session.getPerun(); - User user = app.getUser(); - Vo vo = app.getVo(); - - // For INITIAL VO APPLICATIONS - if (Application.AppType.INITIAL.equals(app.getType()) && app.getGroup() == null) { - - Attribute a = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_VIRT + ":eduPersonScopedAffiliations"); - - if (a.getValue() != null) { - - Member member = perun.getMembersManagerBl().getMemberByUser(session, vo, user); - - List affiliations = a.valueAsList(); - - if (affiliations.contains("employee@cvut.cz")) { - categorizeMember(session, vo, member, "Osoby:CVUT:Zamestnanec"); - } - if (affiliations.contains("student@cvut.cz")) { - categorizeMember(session, vo, member, "Osoby:CVUT:Student"); - } - if (affiliations.contains("employee@cuni.cz")) { - categorizeMember(session, vo, member, "Osoby:UK:Zamestnanec"); - } - if (affiliations.contains("student@cuni.cz")) { - categorizeMember(session, vo, member, "Osoby:UK:Student"); - } - - } - - } - - return app; - - } - - private void categorizeMember(PerunSession session, Vo vo, Member member, String groupName) { - - PerunBl perun = (PerunBl)session.getPerun(); - Group group = null; - try { - group = perun.getGroupsManagerBl().getGroupByName(session, vo, groupName); - perun.getGroupsManagerBl().addMember(session, group, member); - } catch (GroupNotExistsException e) { - log.warn("Destination group {} for ČVUT FBMI and UK members categorisation doesn't exists.", groupName, e); - } catch (WrongReferenceAttributeValueException | WrongAttributeValueException e) { - log.error("Member {} couldn't be added to expected group {}.", member, group, e); - } catch (AlreadyMemberException e) { - // IGNORE - } - - } - -} diff --git a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java index 073a7a4ff1..6ab320c312 100644 --- a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java +++ b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/AppAutoRejectionSchedulerTest.java @@ -9,7 +9,6 @@ import cz.metacentrum.perun.core.api.Vo; import cz.metacentrum.perun.core.api.exceptions.GroupExistsException; import cz.metacentrum.perun.core.impl.Auditer; -import cz.metacentrum.perun.core.impl.Compatibility; import cz.metacentrum.perun.registrar.impl.AppAutoRejectionScheduler; import cz.metacentrum.perun.registrar.model.Application; import cz.metacentrum.perun.registrar.model.ApplicationForm; @@ -85,6 +84,7 @@ public void setUp() throws Exception { setUpVo(); setApplicationUser(); + ReflectionTestUtils.setField(spyScheduler.getPerun(), "auditer", auditerMock); } @@ -107,7 +107,7 @@ public void checkApplicationsExpirationForGroup() throws Exception { System.out.println(CLASS_NAME + "checkApplicationsExpirationForGroup"); // set up expired application and reject it - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); spyScheduler.checkApplicationsExpiration(); @@ -147,7 +147,7 @@ public void checkVoApplicationShouldNotBeAutoRejected() throws Exception { public void checkGroupApplicationShouldBeAutoRejected() throws Exception { System.out.println(CLASS_NAME + "checkGroupApplicationShouldBeAutoRejected"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -161,7 +161,7 @@ public void checkGroupApplicationShouldBeAutoRejected() throws Exception { public void checkGroupApplicationShouldNotBeAutoRejected() throws Exception { System.out.println(CLASS_NAME + "checkGroupApplicationShouldNotBeAutoRejected"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application submitApp = setUpAndSubmitAppForPotentialAutoRejection(50, group, GROUP_APP_EXP_RULES); @@ -172,6 +172,31 @@ public void checkGroupApplicationShouldNotBeAutoRejected() throws Exception { assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.VERIFIED); } + @Test + public void checkGroupApplicationsRejectedWhenVoApplicationRejected() throws Exception { + System.out.println(CLASS_NAME + "checkGroupApplicationsRejectedWhenVoApplicationRejected"); + + Group group1 = createGroup("Group1"); + Group group2 = createGroup("Group2"); + + Application expiredVoApp = setUpAndSubmitAppForPotentialAutoRejection(70, null, VO_APP_EXP_RULES); + + Application nonExpiredGroupApp = setUpAndSubmitAppForPotentialAutoRejection(50, group1, GROUP_APP_EXP_RULES); + Application expiredGroupApp = setUpAndSubmitAppForPotentialAutoRejection(70, group2, GROUP_APP_EXP_RULES); + + ReflectionTestUtils.invokeMethod(spyScheduler, "voApplicationsAutoRejection", Collections.singletonList(vo)); + + // check results + Application returnedApp = registrarManager.getApplicationById(session, expiredVoApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + + returnedApp = registrarManager.getApplicationById(session, nonExpiredGroupApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + + returnedApp = registrarManager.getApplicationById(session, expiredGroupApp.getId()); + assertEquals("Application should be rejected.", returnedApp.getState(), Application.AppState.REJECTED); + } + @Test public void voAdminIgnoredCustomMessage() throws Exception { System.out.println(CLASS_NAME + "voAdminIgnoredCustomMessage"); @@ -242,7 +267,7 @@ public void voEmailVerificationDefaultMessage() throws Exception { public void groupAdminIgnoredCustomMessage() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredCustomMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -260,7 +285,7 @@ public void groupAdminIgnoredCustomMessage() throws Exception { public void groupAdminIgnoredDefaultMessage() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredDefaultMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -276,7 +301,7 @@ public void groupAdminIgnoredDefaultMessage() throws Exception { public void groupEmailVerificationCustomMessage() throws Exception { System.out.println(CLASS_NAME + "groupEmailVerificationCustomMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -297,7 +322,7 @@ public void groupEmailVerificationCustomMessage() throws Exception { public void groupEmailVerificationDefaultMessage() throws Exception { System.out.println(CLASS_NAME + "groupEmailVerificationDefaultMessage"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -398,7 +423,7 @@ public void voMailVerificationCustomMessageDefault() throws Exception { public void groupAdminIgnoredCustomMessageDefault() throws Exception { System.out.println(CLASS_NAME + "groupAdminIgnoredCustomMessageDefault"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -420,7 +445,7 @@ public void groupAdminIgnoredCustomMessageDefault() throws Exception { public void groupMailVerificationCustomMessageDefault() throws Exception { System.out.println(CLASS_NAME + "groupMailVerificationCustomMessageDefault"); - Group group = createGroup(); + Group group = createGroup("Group for apply"); Application application = setUpAndSubmitAppForPotentialAutoRejection(70, group, GROUP_APP_EXP_RULES); @@ -467,9 +492,9 @@ private void setApplicationUser() { * @return created group * @throws GroupExistsException if group already exists */ - private Group createGroup() throws GroupExistsException { + private Group createGroup(String name) throws GroupExistsException { Group group = new Group(); - group.setName("Group for apply"); + group.setName(name); return perun.getGroupsManagerBl().createGroup(session, vo, group); } diff --git a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java index 4aeac3db14..590a443e3b 100644 --- a/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java +++ b/perun-registrar-lib/src/test/java/cz/metacentrum/perun/registrar/RegistrarBaseIntegrationTest.java @@ -1239,6 +1239,32 @@ public void getApplicationsPageApplicationFormSearch() throws Exception { assertThat(result.getData().get(0).getFormData().size()).isEqualTo(2); } + @Test + public void invitationFormExistsForGroup() throws Exception { + Group groupWithInvitation = perun.getGroupsManagerBl().createGroup(session, vo, new Group("group1", "group with form")); + Group groupWithoutInvitation = perun.getGroupsManagerBl().createGroup(session, vo, new Group("group2", "group without form")); + + registrarManager.createApplicationFormInGroup(session, groupWithInvitation); + ApplicationForm form = registrarManager.getFormForGroup(groupWithInvitation); + ApplicationMail mail = new ApplicationMail(0, INITIAL, form.getId(), MailType.USER_INVITE, true); + mailManager.addMail(session, form, mail); + + assertTrue(mailManager.invitationFormExists(session, vo, groupWithInvitation)); + assertFalse(mailManager.invitationFormExists(session, vo, groupWithoutInvitation)); + } + + @Test + public void invitationFormExistsForVo() throws Exception { + Vo voWithoutInvitation = perun.getVosManager().createVo(session, new Vo(1234, "test", "test")); + + ApplicationForm form = registrarManager.getFormForVo(vo); + ApplicationMail mail = new ApplicationMail(0, INITIAL, form.getId(), MailType.USER_INVITE, true); + mailManager.addMail(session, form, mail); + + assertTrue(mailManager.invitationFormExists(session, vo, null)); + assertFalse(mailManager.invitationFormExists(session, voWithoutInvitation, null)); + } + @Test public void getApplicationsPageMultipleFormItems() throws Exception { System.out.println("getApplicationsPageMultipleFormItems"); diff --git a/perun-rpc/pom.xml b/perun-rpc/pom.xml index c1ea63faf8..cf128e2ddc 100644 --- a/perun-rpc/pom.xml +++ b/perun-rpc/pom.xml @@ -34,7 +34,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.4 + 1.10.5 tomcat9x diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java index ba9b710cc8..4d1b1da9cd 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java @@ -166,11 +166,11 @@ public Member call(ApiCaller ac, Deserializer parms) throws PerunException { * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. - * @param sendActivationLink (optional) boolean if true link for manual activation of account will be send to the email + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. + * @param sendActivationLink boolean (optional) If true link for manual activation of account will be sent to the email * default is false, can't be used with empty email parameter * If set to true, a non-empty namespace has to be provided. - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. * @return RichMember newly created sponsored member */ createSponsoredMember { @@ -275,7 +275,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param password String password * @param login String login * @param sponsor int id of sponsoring user - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return RichMember sponsored member */ /*# @@ -289,7 +289,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param namespace String used for selecting external system in which guest user account will be created * @param password String password * @param login String login - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return RichMember sponsored member */ setSponsoredMember { @@ -332,7 +332,7 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * * If the sponsor is not specified, the current principal becomes the SPONSOR, if he has such privileges. * - * Since there may be error while creating some of the members and we cannot simply rollback the transaction and + * Since there may be error while creating some members, and we cannot simply roll back the transaction and * start over, exceptions during member creation are not thrown and the returned list has this structure: * * [{"name" -> name, "status" -> "OK" or "Error...", "login" -> login, "password" -> password}] @@ -351,11 +351,11 @@ public RichMember call(ApiCaller ac, Deserializer params) throws PerunException * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. - * @param sendActivationLinks (optional) boolean if true link for manual activation of every created sponsored member - * account will be send to the email (can't be used with empty email parameter), default is false + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. + * @param sendActivationLinks boolean (optional) If true link for manual activation of every created sponsored member + * account will be sent to the email (can't be used with empty email parameter), default is false * If set to true, a non-empty namespace has to be provided. - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. * @param groups int[] group ids, to which will be the created users assigned (has to be from the given vo) * @return List> newly created sponsored member, their password and status of creation */ @@ -416,7 +416,7 @@ public List> call(ApiCaller ac, Deserializer params) throws * Can be called either by a user with role SPONSOR, in that case the user becomes the sponsor, * or by a user with role REGISTRAR that must specify the sponsoring user using ID. * - * Since there may be error while creating some of the members and we cannot simply rollback the transaction and start over, + * Since there may be error while creating some members, and we cannot simply roll back the transaction and start over, * exceptions during member creation are not thrown and the returned list has this structure: * * [{"name" -> name, "status" -> "OK" or "Error...", "login" -> login, "password" -> password}] @@ -431,12 +431,12 @@ public List> call(ApiCaller ac, Deserializer params) throws * @param vo int VO ID * @param namespace String namespace selecting remote system for storing the password * @param sponsor int sponsor's ID - * @param email (optional) preferred email that will be set to the created user. If no email - * is provided, "no-reply@muni.cz" is used. - * @param sendActivationLink (optional) boolean if true link for manual activation of every created sponsored member account will be send + * @param email String (optional) preferred email that will be set to the created user. If no email + * is provided, default from the instance config is used (usually some kind of "no-reply" address). + * @param sendActivationLink boolean (optional) If true link for manual activation of every created sponsored member account will be sent * to the email, be careful when using with empty (no-reply) email, default is false - * @param language language of the activation email (e.g. "en", "cs"). Use english if null. - * @param validityTo (Optional) String the last day, when the sponsorship is active, yyyy-mm-dd format. + * @param language String Language of the activation email (e.g. "en", "cs"). Use english if null. + * @param validityTo String (optional) The last day, when the sponsorship is active, yyyy-mm-dd format. * @return List> newly created sponsored member, their password and status of creation */ createSponsoredMembers { @@ -2037,5 +2037,39 @@ public Object call(ApiCaller ac, Deserializer parms) throws PerunException { parms.read("query", MembersPageQuery.class), parms.readList("attrNames", String.class)); } + }, + + /*# + * Add member candidates. + * + * @param vo int Vo id + * @param candidates List Member candidates + */ + /*# + * Add member candidates to group. + * + * @param vo int Vo id + * @param candidates List Member candidates + * @param group int Group id + */ + addMemberCandidates { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + if (parms.contains("group")) { + ac.getMembersManager().addMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.readList("candidates", MemberCandidate.class), + ac.getGroupById(parms.readInt("group")) + ); + } else { + ac.getMembersManager().addMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.readList("candidates", MemberCandidate.class) + ); + } + return null; + } } } diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java index a39d9c3973..435ceb0fde 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/RegistrarManagerMethod.java @@ -138,6 +138,54 @@ public Void call(ApiCaller ac, Deserializer parms) throws PerunException { }, + /*# + * Invite member candidates. If candidate contains richUser, then his preferred mail is retrieved and used, otherwise email must be passed in candidate's attributes. + * + * @param vo Vo id + * @param lang language + * @param candidates List list of member candidates + */ + /*# + * Invite member candidates to group. If candidate contains richUser, then his preferred mail is retrieved and used, otherwise email must be passed in candidate's attributes. + * + * @param vo Vo id + * @param lang language + * @param candidates List list of member candidates + * @param group Group id + */ + inviteMemberCandidates { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + ac.getRegistrarManager().inviteMemberCandidates( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.contains("group") ? ac.getGroupById(parms.readInt("group")) : null, + parms.readString("lang"), + parms.readList("candidates", MemberCandidate.class) + ); + return null; + } + }, + + /*# + * Checks if invitation form exists. + * + * @param vo Vo id + * @param group Group id + * + * @return true if invitation form exists, false otherwise + */ + invitationFormExists { + @Override + public Boolean call(ApiCaller ac, Deserializer parms) throws PerunException { + return ac.getRegistrarManager().getMailManager().invitationFormExists( + ac.getSession(), + ac.getVoById(parms.readInt("vo")), + parms.contains("group") ? ac.getGroupById(parms.readInt("group")) : null + ); + } + }, + /*# * Send invitations with link to VO / Group application form from provided csv data * diff --git a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl index 89f7e0491f..8800149fe2 100755 --- a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl +++ b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl @@ -93,11 +93,11 @@ $objectExamples{"List<RichGroup>"} = $listPrepend . $objectExamples{"RichGroup"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichGroup>"}; -$objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; +$objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; $objectExamples{"List<Application>"} = $listPrepend . $objectExamples{"Application"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Application>"}; -$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; +$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; $objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java index 95a0787c1a..4c2e1ae391 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/model/Application.java @@ -179,6 +179,24 @@ public final native void setExtSourceName(String extSourceName) /*-{ this.extSourceName = extSourceName; }-*/; + /** + * Get error that occurred during the automatic approval of the application. + * + * @return error + */ + public final native String getAutoApproveError() /*-{ + return this.autoApproveError; + }-*/; + + /** + * Set error that occurred during the automatic approval of the application. + * + * @param autoApproveError error + */ + public final native void setAutoApproveError(String autoApproveError) /*-{ + this.autoApproveError = autoApproveError; + }-*/; + /** * Get extSourceType * @return extSourceType diff --git a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java index d9be0f64c4..3668606c51 100644 --- a/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java +++ b/perun-web-gui/src/main/java/cz/metacentrum/perun/webgui/tabs/registrartabs/CreateMailTabItem.java @@ -324,6 +324,7 @@ private Widget availableTagsTab() { "
{mailFooter} - common mail footer defined by VO" + "
{errors} - errors description, what happened while processing new application. Useful for VO administrators." + "
{customMessage} - optional message passed by administrators when rejecting an application" + + "
{autoApproveError} - error that caused automatic approval failure" + "
{fromApp-itemName} - value of a form item in user's application. You MUST specify the itemName, e.g. {fromApp-mail} will print value of item with short name 'mail' from user's application." + "

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

User related:" + diff --git a/pom.xml b/pom.xml index 5e561ded80..d9b993581b 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 1.6 0.5.10 9.1.22 - directory_v1-rev20230103-2.0.0 + directory_v1-rev20230124-2.0.0 2.2.21.Final 3.2.10.Final 1.1.0.GA