From de6b0fe09fb4d86e99e0d31700cf1a2a86716a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hako=C5=A1?= Date: Tue, 1 Nov 2022 12:34:52 +0100 Subject: [PATCH 1/3] feat(core): optional login for namespace * urn allows login to be null or have a value if present * any namespace can use optional namespace --- .../core/blImpl/AttributesManagerBlImpl.java | 17 +++++ ...ute_def_virt_optional_login_namespace.java | 48 ++++++++++++++ ...def_virt_optional_login_namespaceTest.java | 63 +++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespace.java create mode 100644 perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespaceTest.java 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 0bc2b4cc08..ffc97ec6f8 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 @@ -110,6 +110,7 @@ import cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_member_attribute_def_virt_isLifecycleAlterable; import cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_member_group_attribute_def_virt_groupStatus; import cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_member_group_attribute_def_virt_groupStatusIndirect; +import cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_user_attribute_def_virt_optional_login_namespace; import cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_applicationAutoRejectMessages; import cz.metacentrum.perun.core.implApi.AttributesManagerImplApi; import cz.metacentrum.perun.core.implApi.modules.attributes.AttributesModuleImplApi; @@ -8149,6 +8150,22 @@ protected void initialize() { policies.add(Triple.of(Role.FACILITYADMIN, READ, RoleObject.Facility)); attributes.put(attr, createInitialPolicyCollections(policies)); + // optional-login-namespace + attr = new AttributeDefinition(); + attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); + attr.setType(String.class.getName()); + attr.setFriendlyName("optional-login-namespace:"+namespace); + attr.setDisplayName("Optional login in namespace: "+namespace); + attr.setDescription("Contains an optional login in namespace" + namespace + " if the user has it."); + + policies = new ArrayList<>(); + policies.add(Triple.of(Role.SELF, READ, RoleObject.User)); + policies.add(Triple.of(Role.VOADMIN, READ, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPADMIN, READ, RoleObject.Vo)); + policies.add(Triple.of(Role.GROUPMEMBERSHIPMANAGER, READ, RoleObject.Vo)); + policies.add(Triple.of(Role.FACILITYADMIN, READ, RoleObject.Facility)); + attributes.put(attr, createInitialPolicyCollections(policies)); + // pwd-reset templates attr = new AttributeDefinition(); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespace.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespace.java new file mode 100644 index 0000000000..3937d75f97 --- /dev/null +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespace.java @@ -0,0 +1,48 @@ +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.User; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import cz.metacentrum.perun.core.implApi.modules.attributes.UserVirtualAttributesModuleAbstract; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Attribute, that contains login, only if it is available in the namespace. + * + * @author Matej Hakoš + */ +public class urn_perun_user_attribute_def_virt_optional_login_namespace extends UserVirtualAttributesModuleAbstract { + private final static Logger log = LoggerFactory.getLogger(urn_perun_user_attribute_def_virt_optional_login_namespace.class); + + @Override + public Attribute getAttributeValue(PerunSessionImpl perunSession, User user, AttributeDefinition attribute) { + Attribute attr = new Attribute(attribute); + String namespace = AttributesManager.NS_USER_ATTR_DEF + ":login-namespace:" + attribute.getFriendlyNameParameter(); + try { + Attribute a = perunSession.getPerunBl().getAttributesManagerBl().getAttribute(perunSession, user, namespace); + attr.setValue(a.getValue()); + } catch (AttributeNotExistsException e) { + // We log the non-existing attribute, but we don't throw an exception. + log.warn("Attribute {} does not exist.", namespace); + } catch (WrongAttributeAssignmentException e) { + // It's OK, we just return attribute with value null + } + return attr; + } + + @Override + public AttributeDefinition getAttributeDefinition() { + AttributeDefinition attr = new AttributeDefinition(); + attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); + attr.setFriendlyName("optional-login-namespace"); + attr.setDisplayName("Optional login in namespace"); + attr.setType(String.class.getName()); + attr.setDescription("Contains an optional login if the user has it."); + return attr; + } +} diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespaceTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespaceTest.java new file mode 100644 index 0000000000..2528c1613b --- /dev/null +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_optional_login_namespaceTest.java @@ -0,0 +1,63 @@ +package cz.metacentrum.perun.core.impl.modules.attributes; + +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.AttributesManager; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.bl.AttributesManagerBl; +import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class urn_perun_user_attribute_def_virt_optional_login_namespaceTest { + + private static urn_perun_user_attribute_def_virt_optional_login_namespace classInstance; + private static PerunSessionImpl session; + private static User user; + private static Attribute attributeToCheck; + + @Before + public void setUp() throws Exception { + classInstance = new urn_perun_user_attribute_def_virt_optional_login_namespace(); + session = mock(PerunSessionImpl.class); + + attributeToCheck = new Attribute(); + attributeToCheck.setNamespace(AttributesManager.NS_USER_ATTR_DEF); + attributeToCheck.setFriendlyName("login-namespace:einfra"); + attributeToCheck.setValue("test"); + + String namespace = AttributesManager.NS_USER_ATTR_DEF + ":login-namespace:" + attributeToCheck.getFriendlyNameParameter(); + when(session.getPerunBl()).thenReturn(mock(PerunBl.class)); + when(session.getPerunBl().getAttributesManagerBl()).thenReturn(mock(AttributesManagerBl.class)); + when(session.getPerunBl().getAttributesManagerBl().getAttribute(session, user, namespace)).thenReturn(attributeToCheck); + } + + @Test + public void testCheckWithAttribute() { + System.out.println("testCheckWithAttribute()"); + attributeToCheck.setValue("test-value"); + + Attribute attr = new Attribute(); + attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); + attr.setFriendlyName("optional-login-namespace:einfra"); + + assertEquals(attributeToCheck.getValue(), classInstance.getAttributeValue(session, user, attr).getValue()); + } + + @Test + public void testCheckWithNull() { + System.out.println("testCheckWithNull()"); + attributeToCheck.setValue(null); + + Attribute attr = new Attribute(); + attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); + attr.setFriendlyName("optional-login-namespace:einfra"); + + assertNull(classInstance.getAttributeValue(session, user, attr).getValue()); + } +} From d42b0efd6294b2ad24619ec9cee5266ca5ab8f43 Mon Sep 17 00:00:00 2001 From: David Flor <493294@mail.muni.cz> Date: Thu, 3 Nov 2022 19:52:40 +0100 Subject: [PATCH 2/3] feat(cli): refactored setRole and unsetRole scripts to work with any role from perun-roles.yml * the scripts now call getAllRolesManagementRules and hence are more flexible, since roles are no longer hard-coded. --- perun-cli/Perun/AuthzResolverAgent.pm | 6 + perun-cli/Perun/Common.pm | 1 + perun-cli/Perun/beans/RoleManagementRules.pm | 293 +++++++++++++++++++ perun-cli/setRole | 219 ++++++-------- perun-cli/unsetRole | 226 ++++++-------- 5 files changed, 480 insertions(+), 265 deletions(-) create mode 100644 perun-cli/Perun/beans/RoleManagementRules.pm diff --git a/perun-cli/Perun/AuthzResolverAgent.pm b/perun-cli/Perun/AuthzResolverAgent.pm index 00f08b5a0d..35eee8e76b 100644 --- a/perun-cli/Perun/AuthzResolverAgent.pm +++ b/perun-cli/Perun/AuthzResolverAgent.pm @@ -68,4 +68,10 @@ sub refreshMfa return Perun::Common::callManagerMethod('refreshMfa', '', @_); } +sub getAllRolesManagementRules +{ + return Perun::Common::callManagerMethod('getAllRolesManagementRules', '[]RoleManagementRules', @_); +} + + 1; diff --git a/perun-cli/Perun/Common.pm b/perun-cli/Perun/Common.pm index 4bd9122b47..65a2368e50 100644 --- a/perun-cli/Perun/Common.pm +++ b/perun-cli/Perun/Common.pm @@ -57,6 +57,7 @@ use Perun::beans::AttributePolicy; use Perun::beans::AttributePolicyCollection; use Perun::beans::AttributeRules; use Perun::beans::OidcConfig; +use Perun::beans::RoleManagementRules; sub newEmptyBean { diff --git a/perun-cli/Perun/beans/RoleManagementRules.pm b/perun-cli/Perun/beans/RoleManagementRules.pm new file mode 100644 index 0000000000..80f71e5f44 --- /dev/null +++ b/perun-cli/Perun/beans/RoleManagementRules.pm @@ -0,0 +1,293 @@ +package Perun::beans::RoleManagementRules; + +use strict; +use warnings; + +use Perun::Common; + +sub new +{ + bless({}); +} + +sub fromHash +{ + return Perun::Common::fromHash(@_); +} + +sub TO_JSON +{ + my $self = shift; + + my $roleName; + if (defined($self->{_roleName})) { + $roleName = "$self->{_roleName}"; + } else { + $roleName = undef; + } + + my $primaryObject; + if (defined($self->{_primaryObject})) { + $primaryObject = "$self->{_primaryObject}"; + } else { + $primaryObject = undef; + } + + my @privilegedRolesToManage; + if (defined($self->{_privilegedRolesToManage})) { + @privilegedRolesToManage = @{%{$self->{_privilegedRolesToManage}}}; + } else { + @privilegedRolesToManage = undef; + } + + my @privilegedRolesToRead; + if (defined($self->{_privilegedRolesToRead})) { + @privilegedRolesToRead = @{%{$self->{_privilegedRolesToRead}}}; + } else { + @privilegedRolesToRead = undef; + } + + my %entitiesToManage; + if (defined($self->{_entitiesToManage})) { + %entitiesToManage = %{$self->{_entitiesToManage}}; + } else { + %entitiesToManage = undef + } + + my %assignedObjects; + if (defined($self->{_assignedObjects})) { + %assignedObjects = %{$self->{_assignedObjects}}; + } else { + %assignedObjects = undef + } + + my @assignmentCheck; + if (defined($self->{_assignmentCheck})) { + @assignmentCheck = @{%{$self->{_assignmentCheck}}}; + } else { + @assignmentCheck = undef; + } + + my @associatedReadRoles; + if (defined($self->{_associatedReadRoles})) { + @associatedReadRoles = @{$self->{_associatedReadRoles}}; + } else { + @associatedReadRoles = undef; + } + + my $assignableToAttributes; + if (defined($self->{_assignableToAttributes})) { + $assignableToAttributes = $self->{_assignableToAttributes}; + } else { + $assignableToAttributes = undef; + } + + my $skipMFA; + if (defined($self->{_skipMFA})) { + $skipMFA = $self->{_skipMFA}; + } else { + $skipMFA = undef; + } + + my $mfaCriticalRole; + if (defined($self->{_mfaCriticalRole})) { + $mfaCriticalRole = $self->{_mfaCriticalRole}; + } else { + $mfaCriticalRole = undef; + } + + my $displayName; + if (defined($self->{_displayName})) { + $displayName = "$self->{_displayName}"; + } else { + $displayName = undef; + } + + return { roleName => $roleName, primaryObject => $primaryObject, privilegedRolesToManage => \@privilegedRolesToManage, + privilegedRolesToRead => \@privilegedRolesToRead, entitiesToManage => \%entitiesToManage, assignedObjects => \%assignedObjects, + assignmentCheck => \@assignmentCheck, associatedReadRoles => \@associatedReadRoles, assignableToAttributes => $assignableToAttributes, + skipMFA => $skipMFA, mfaCriticalRole => $mfaCriticalRole, displayName => $displayName }; + +} + +sub getRoleName +{ + my $self = shift; + + return $self->{_roleName}; +} + +sub setRoleName +{ + my $self = shift; + $self->{_roleName} = shift; + + return; +} + +sub getPrimaryObject +{ + my $self = shift; + + return $self->{_primaryObject}; +} + +sub setPrimaryObject +{ + my $self = shift; + $self->{_primaryObject} = shift; + + return; +} + +sub getPrivilegedRolesToManage +{ + my $self = shift; + + return @{$self->{_privilegedRolesToManage}}; +} + +sub setPrivilegedRolesToManage +{ + my $self = shift; + $self->{_privilegedRolesToManage} = shift; + + return; +} + +sub getPrivilegedRolesToRead +{ + my $self = shift; + + return @{$self->{_privilegedRolesToRead}}; +} + +sub setPrivilegedRolesToRead +{ + my $self = shift; + $self->{_privilegedRolesToRead} = shift; + + return; +} + +sub getEntitiesToManage +{ + my $self = shift; + + return $self->{_entitiesToManage}; +} + +sub setEntitiesToManage +{ + my $self = shift; + $self->{_entitiesToManage} = shift; + + return; +} + +sub getAssignedObjects +{ + my $self = shift; + + return $self->{_assignedObjects}; +} + +sub setAssignedObjects +{ + my $self = shift; + $self->{_assignedObjects} = shift; + + return; +} + +sub getAssignmentCheck +{ + my $self = shift; + + return @{$self->{_assignmentCheck}}; +} + +sub setAssignmentCheck +{ + my $self = shift; + $self->{_assignmentCheck} = shift; + + return; +} + +sub getAssociatedReadRoles +{ + my $self = shift; + + return @{$self->{_associatedReadRoles}}; +} + +sub setAssociatedReadRoles +{ + my $self = shift; + $self->{_associatedReadRoles} = shift; + + return; +} + +sub getAssignableToAttributes +{ + my $self = shift; + + return $self->{_assignableToAttributes}; +} + +sub setAssignableToAttributes +{ + my $self = shift; + $self->{_assignableToAttributes} = shift; + + return; +} + +sub getSkipMFA +{ + my $self = shift; + + return $self->{_skipMFA}; +} + +sub setSkipMFA +{ + my $self = shift; + $self->{_skipMFA} = shift; + + return; +} + +sub getMfaCriticalRole +{ + my $self = shift; + + return $self->{_mfaCriticalRole}; +} + +sub setMfaCriticalRole +{ + my $self = shift; + $self->{_mfaCriticalRole} = shift; + + return; +} + +sub getDisplayName +{ + my $self = shift; + + return $self->{_displayName}; +} + +sub setDisplayName +{ + my $self = shift; + $self->{_displayName} = shift; + + return; +} + +1; \ No newline at end of file diff --git a/perun-cli/setRole b/perun-cli/setRole index 969a86524b..77cd95b8f6 100755 --- a/perun-cli/setRole +++ b/perun-cli/setRole @@ -2,170 +2,129 @@ use strict; use warnings; +use Switch; use Getopt::Long qw(:config no_ignore_case); use Perun::Agent; use Perun::Common qw(printMessage); + sub help { return qq{ - Adds Role to user or authorizedGroup. User or group is required. - Role is required. Some complementaryObject is required if not PERUNADMIN. - For now only 1 object is supported to be set at once. + Adds role to user or authorizedGroup. User or group is required. + Name of the role to set in all caps with no spaces is required (e.g. GROUPADMIN). + Unless the role does not have a complementary object (e.g. PERUNADMIN), + the type of complementary object (e.g. Vo, Resource, Facility) to set is required along with its id. ------------------------------------ Available options: --userId | -u user id --authorizedGroupId | -a authorizedGroup id - --role | -R role name - --facilityId | -f facility comp object id - --groupId | -g group comp object id - --voId | -v vo comp object id - --resourceId | -r resource comp object id - --serviceId | -s service comp object id + --role | -r role name + --compObject | -o object name + --objectId | -i object id --batch | -b batch --help | -h prints this help - }; +}; } our $batch; -my ($userId, $authorizedGroupId, $role, $facilityId, $groupId, $voId, $resourceId, $serviceId); +my ($userId, $authorizedGroupId, $role, $compObjectName, $objectId); + GetOptions ("help|h" => sub { print help(); exit 0; }, "batch|b" => \$batch, "userId|u=i" => \$userId, "authorizedGroupId|a=i" => \$authorizedGroupId, - "role|R=s" => \$role, - "facilityId|f=i" => \$facilityId, - "groupId|g=i" => \$groupId, - "voId|v=i" => \$voId, - "resourceId|r=i" => \$resourceId, - "serviceId|s=i" => \$serviceId) || die help(); + "role|r=s" => \$role, + "compObject|o=s" => \$compObjectName, + "objectId|i=i" => \$objectId) || die help(); -my $agent = Perun::Agent->new(); -my $authzResolverAgent = $agent->getAuthzResolverAgent; -#Check options -#one of userId or authorizedGroupId must be set -if (!defined($userId) && !defined($authorizedGroupId)) { die "ERROR: one of userId and authorizedGroupId is requried\n";} -if (defined($userId) && defined($authorizedGroupId)) { die "ERROR: only one of userId and authorizedGroupId must be set at one moment\n";} +# One of userId or authorizedGroupId must be set +if (!defined($userId) && !defined($authorizedGroupId)) { die "ERROR: one of userId and authorizedGroupId is required\n"; } +if (defined($userId) && defined($authorizedGroupId)) { die "ERROR: only one of userId and authorizedGroupId must be set at one moment\n"; } -#role must be set -unless (defined($role)) { die "ERROR: role name is required"}; +# Role must be set +unless (defined($role)) { die "ERROR: role name is required\n" }; if ($role !~ /^[A-Z]+$/) { die "ERROR: role must be one word in uppercase format\n"; } -#if role is perunadmin, no other object need to be set -if ($role eq "PERUNADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, role => $role ); - printMessage("PERUNADMIN role is successfully set for user $userId", $batch); - } else { - die "ERROR: PERUNADMIN role cannot be set for group \n"; - } -} elsif ($role eq "PERUNOBSERVER") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, role => $role ); - printMessage("PERUNOBSERVER role is successfully set for user $userId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, role => $role ); - printMessage("PERUNOBSERVER role is successfully set for authorizedGroup $authorizedGroupId", $batch); - } -} elsif ($role eq "AUDITCONSUMERADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, role => $role ); - printMessage("AUDITCONSUMERADMIN role is successfully set for user $userId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, role => $role ); - printMessage("AUDITCONSUMERADMIN role is successfully set for authorizedGroup $authorizedGroupId", $batch); - } -} elsif ($role eq "CABINETADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, role => $role ); - printMessage("CABINETADMIN role is successfully set for user $userId", $batch); - } else { - die "ERROR: CABINETADMIN role cannot be set for group \n"; - } - #if there is other role, only one complementary object is needed -} else { - #facility - if (defined($facilityId) && !defined($groupId) && !defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $facilitiesAgent = $agent->getFacilitiesAgent; - my $facility = $facilitiesAgent->getFacilityById( id => $facilityId ); - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, complementaryObject => $facility, role => $role ); - printMessage("$role role is successfully set for user $userId and facility $facilityId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, complementaryObject => $facility, role => $role ); - printMessage("$role role is successfully set for authorizedGroup $authorizedGroupId and facility $facilityId", $batch); - } - #group - } elsif (!defined($facilityId) && defined($groupId) && !defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $groupsAgent = $agent->getGroupsAgent; - my $group = $groupsAgent->getGroupById( id => $groupId ); - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, complementaryObject => $group, role => $role ); - printMessage("$role role is successfully set for user $userId and group $groupId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, complementaryObject => $group, role => $role ); - printMessage("$role role is successfully set for authorizedGroup $authorizedGroupId and group $groupId", $batch); + +my $agent = Perun::Agent->new(); +my $authzResolverAgent = $agent->getAuthzResolverAgent(); +my @rules = $authzResolverAgent->getAllRolesManagementRules(); + +foreach (@rules) { + my $rule = $_; + if ($rule->getRoleName() eq $role) { + my $primObject = $rule->getPrimaryObject(); + if (!defined($primObject)) { + if (defined($compObjectName)) { + die "ERROR: role $role doesn't have a complementary object\n"; + } + } elsif (!defined($compObjectName) || !defined($objectId) || lc $primObject ne lc $compObjectName) { + die "ERROR: role $role requires object $primObject"; } - #vo - } elsif (!defined($facilityId) && !defined($groupId) && defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $vosAgent = $agent->getVosAgent; - my $vo = $vosAgent->getVoById( id => $voId ); + if (defined($userId)) { - if ("SPONSOR" eq $role) { - $vosAgent->addSponsorRole( user => $userId, vo => $vo->getId()); + if (!exists($rule->getEntitiesToManage()->{User})) { + die "ERROR: $role is not assignable to user\n"; + } + if (defined($primObject)) { + $authzResolverAgent->setRole(user => $userId, complementaryObject => getObject(), role => $role); } else { - $authzResolverAgent->setRole( user => $userId, complementaryObject => $vo, role => $role ); + $authzResolverAgent->setRole(user => $userId, role => $role); + } + my $displayName = $rule->getDisplayName(); + printMessage("Role $displayName successfully set for user $userId", $batch); + exit 0; + } elsif (defined($authorizedGroupId)) { + if (!exists($rule->getEntitiesToManage->{Group})) { + die "ERROR: $role is not assignable to group\n"; } - printMessage("$role role is successfully set for user $userId and VO $voId", $batch); - } else { - if ("SPONSOR" eq $role) { - $vosAgent->addSponsorRole( authorizedGroup => $authorizedGroupId, vo => $vo->getId()); + if (defined($primObject)) { + $authzResolverAgent->setRole(authorizedGroup => $authorizedGroupId, complementaryObject => getObject(), role => $role); } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, complementaryObject => $vo, role => $role ); + $authzResolverAgent->setRole(authorizedGroup => $authorizedGroupId, role => $role); } - printMessage("$role role is successfully set for authorizedGroup $authorizedGroupId and VO $voId", $batch); - } - #resource - } elsif (!defined($facilityId) && !defined($groupId) && !defined($voId) && defined($resourceId) && !defined($serviceId)) { - my $resourcesAgent = $agent->getResourcesAgent; - my $resource = $resourcesAgent->getResourceById( id => $resourceId ); - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, complementaryObject => $resource, role => $role ); - printMessage("$role role is successfully set for user $userId and resource $resourceId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, complementaryObject => $resource, role => $role ); - printMessage("$role role is successfully set for authorizedGroup $authorizedGroupId and resource $resourceId", $batch); + my $displayName = $rule->getDisplayName(); + printMessage("Role $displayName successfully set for group $authorizedGroupId", $batch); + exit 0; } - #serviceId - } elsif (!defined($facilityId) && !defined($groupId) && !defined($voId) && !defined($resourceId) && defined($serviceId)) { - my $servicesAgent = $agent->getServicesAgent; - my $service = $servicesAgent->getServiceById( id => $serviceId ); - if (defined($userId)) { - $authzResolverAgent->setRole( user => $userId, complementaryObject => $service, role => $role ); - printMessage("$role role is successfully set for user $userId and service $serviceId", $batch); - } else { - $authzResolverAgent->setRole( authorizedGroup => $authorizedGroupId, complementaryObject => $service, role => $role ); - printMessage("$role role is successfully set for authorizedGroup $authorizedGroupId and service $serviceId", $batch); - } - #no object or more than one - } else { - die "ERROR: only one of objects (facility,vo,group,resource,service) id need to be set\n"; -} + } } +die "ERROR: role $role not found\n"; + +sub getObject { + switch(lc $compObjectName) { + case "vo" { + my $vosAgent = $agent->getVosAgent; + return $vosAgent->getVoById( id => $objectId ); + } + case "facility" { + my $facilitiesAgent = $agent->getFacilitiesAgent; + return $facilitiesAgent->getFacilityById( id => $objectId ); + } + case "group" { + my $groupsAgent = $agent->getGroupsAgent; + return $groupsAgent->getGroupById( id => $objectId ); + } + case "resource" { + my $resourcesAgent = $agent->getResourcesAgent; + return $resourcesAgent->getResourceById( id => $objectId ); + } + case "service" { + my $servicesAgent = $agent->getServicesAgent; + return $servicesAgent->getServiceById( id => $objectId ); + } + case "securityteam" { + my $securityTeamsAgent = $agent->getSecurityTeamsAgent; + return $securityTeamsAgent->getSecurityTeamById( id => $objectId ); + } + else { + die "ERROR: Invalid object name\n"; + } + } +} \ No newline at end of file diff --git a/perun-cli/unsetRole b/perun-cli/unsetRole index 0e4ca48989..dfe418127b 100755 --- a/perun-cli/unsetRole +++ b/perun-cli/unsetRole @@ -2,173 +2,129 @@ use strict; use warnings; +use Switch; use Getopt::Long qw(:config no_ignore_case); use Perun::Agent; use Perun::Common qw(printMessage); + sub help { return qq{ - Remove Role from user or authorizedGroup. User or group is required. - Role is required. Some complementaryObject is required if not PERUNADMIN. - For now only 1 object is supported to be set at once. + Remove role from user or authorizedGroup. User or group is required. + Name of the role to remove in all caps with no spaces is required (e.g. GROUPADMIN). + Unless the role does not have a complementary object (e.g. PERUNADMIN), + the type of complementary object (e.g. Vo, Resource, Facility) to remove is required along with its id. ------------------------------------ Available options: --userId | -u user id --authorizedGroupId | -a authorizedGroup id - --role | -R role name - --facilityId | -f facility comp object id - --groupId | -g group comp object id - --voId | -v vo comp object id - --resourceId | -r resource comp object id - --serviceId | -s service comp object id + --role | -r role name + --compObject | -o object name + --objectId | -i object id --batch | -b batch --help | -h prints this help - }; +}; } our $batch; -my ($userId, $authorizedGroupId, $role, $facilityId, $groupId, $voId, $resourceId, $serviceId); +my ($userId, $authorizedGroupId, $role, $compObjectName, $objectId); + GetOptions ("help|h" => sub { - print help(); - exit 0; - }, "batch|b" => \$batch, + print help(); + exit 0; +}, "batch|b" => \$batch, "userId|u=i" => \$userId, "authorizedGroupId|a=i" => \$authorizedGroupId, - "role|R=s" => \$role, - "facilityId|f=i" => \$facilityId, - "groupId|g=i" => \$groupId, - "voId|v=i" => \$voId, - "resourceId|r=i" => \$resourceId, - "serviceId|s=i" => \$serviceId) || die help(); + "role|r=s" => \$role, + "compObject|o=s" => \$compObjectName, + "objectId|i=i" => \$objectId) || die help(); -my $agent = Perun::Agent->new(); -my $authzResolverAgent = $agent->getAuthzResolverAgent; -#Check options -#one of userId or authorizedGroupId must be set -if (!defined($userId) && !defined($authorizedGroupId)) { die "ERROR: one of userId and authorizedGroupId is requried\n";} -if (defined($userId) && defined($authorizedGroupId)) { die "ERROR: only one of userId and authorizedGroupId must be set at one moment\n";} +# One of userId or authorizedGroupId must be set +if (!defined($userId) && !defined($authorizedGroupId)) { die "ERROR: one of userId and authorizedGroupId is required\n"; } +if (defined($userId) && defined($authorizedGroupId)) { die "ERROR: only one of userId and authorizedGroupId must be set at one moment\n"; } -#role must be set -unless (defined($role)) { die "ERROR: role name is required"}; +# Role must be set +unless (defined($role)) { die "ERROR: role name is required\n" }; if ($role !~ /^[A-Z]+$/) { die "ERROR: role must be one word in uppercase format\n"; } -#if role is perunadmin, no other object need to be set -if ($role eq "PERUNADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, role => $role ); - printMessage("PERUNADMIN role was removed from user $userId", $batch); - } else { - die "ERROR: PERUNADMIN role cannot be unset from group \n"; - } -} elsif ($role eq "PERUNOBSERVER") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, role => $role ); - printMessage("PERUNOBSERVER role was removed from user $userId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, role => $role ); - printMessage("PERUNOBSERVER role was removed from authorizedGroup $authorizedGroupId", $batch); - } - #if there is other role, only one complementary object is needed -} elsif ($role eq "AUDITCONSUMERADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, role => $role ); - printMessage("AUDITCONSUMERADMIN role was removed from user $userId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, role => $role ); - printMessage("AUDITCONSUMERADMIN role was removed from authorizedGroup $authorizedGroupId", $batch); - } - #if there is other role, only one complementary object is needed -} elsif ($role eq "CABINETADMIN") { - if (defined($facilityId) || defined($voId) || defined($groupId) || defined($resourceId) || defined($serviceId)) { - die "ERROR: no other object needed when setting role $role\n"; - } - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, role => $role ); - printMessage("CABINETADMIN role was removed from user $userId", $batch); - } else { - die "ERROR: CABINETADMIN role cannot be unset from group \n"; - } - #if there is other role, only one complementary object is needed -} else { - #facility - if (defined($facilityId) && !defined($groupId) && !defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $facilitiesAgent = $agent->getFacilitiesAgent; - my $facility = $facilitiesAgent->getFacilityById( id => $facilityId ); - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, complementaryObject => $facility, role => $role ); - printMessage("$role role was removed from user $userId and facility $facilityId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, complementaryObject => $facility, role => $role ); - printMessage("$role role was removed from authorizedGroup $authorizedGroupId and facility $facilityId", $batch); - } - #group - } elsif (!defined($facilityId) && defined($groupId) && !defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $groupsAgent = $agent->getGroupsAgent; - my $group = $groupsAgent->getGroupById( id => $groupId ); - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, complementaryObject => $group, role => $role ); - printMessage("$role role was removed from user $userId and group $groupId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, complementaryObject => $group, role => $role ); - printMessage("$role role was removed from authorizedGroup $authorizedGroupId and group $groupId", $batch); + +my $agent = Perun::Agent->new(); +my $authzResolverAgent = $agent->getAuthzResolverAgent(); +my @rules = $authzResolverAgent->getAllRolesManagementRules(); + +foreach (@rules) { + my $rule = $_; + if ($rule->getRoleName() eq $role) { + my $primObject = $rule->getPrimaryObject(); + if (!defined($primObject)) { + if (defined($compObjectName)) { + die "ERROR: role $role doesn't have a complementary object\n"; + } + } elsif (!defined($compObjectName) || !defined($objectId) || lc $primObject ne lc $compObjectName) { + die "ERROR: role $role requires object $primObject"; } - #vo - } elsif (!defined($facilityId) && !defined($groupId) && defined($voId) && !defined($resourceId) && !defined($serviceId)) { - my $vosAgent = $agent->getVosAgent; - my $vo = $vosAgent->getVoById( id => $voId ); + if (defined($userId)) { - if ("SPONSOR" eq $role) { - $vosAgent->removeSponsorRole( user => $userId, vo => $vo->getId()); + if (!exists($rule->getEntitiesToManage()->{User})) { + die "ERROR: $role is not assignable to user\n"; + } + if (defined($primObject)) { + $authzResolverAgent->unsetRole(user => $userId, complementaryObject => getObject(), role => $role); } else { - $authzResolverAgent->unsetRole( user => $userId, complementaryObject => $vo, role => $role ); + $authzResolverAgent->unsetRole(user => $userId, role => $role); + } + my $displayName = $rule->getDisplayName(); + printMessage("Role $displayName successfully removed from user $userId", $batch); + exit 0; + } elsif (defined($authorizedGroupId)) { + if (!exists($rule->getEntitiesToManage->{Group})) { + die "ERROR: $role is not assignable to group\n"; } - printMessage("$role role was removed from user $userId and VO $voId", $batch); - } else { - if ("SPONSOR" eq $role) { - $vosAgent->removeSponsorRole( authorizedGroup => $authorizedGroupId, vo => $vo->getId()); + if (defined($primObject)) { + $authzResolverAgent->unsetRole(authorizedGroup => $authorizedGroupId, complementaryObject => getObject(), role => $role); } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, complementaryObject => $vo, role => $role ); + $authzResolverAgent->unsetRole(authorizedGroup => $authorizedGroupId, role => $role); } - - printMessage("$role role was removed from authorizedGroup $authorizedGroupId and VO $voId", $batch); + my $displayName = $rule->getDisplayName(); + printMessage("Role $displayName successfully removed from group $authorizedGroupId", $batch); + exit 0; } - #resource - } elsif (!defined($facilityId) && !defined($groupId) && !defined($voId) && defined($resourceId) && !defined($serviceId)) { - my $resourcesAgent = $agent->getResourcesAgent; - my $resource = $resourcesAgent->getResourceById( id => $resourceId ); - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, complementaryObject => $resource, role => $role ); - printMessage("$role role was removed from user $userId and resource $resourceId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, complementaryObject => $resource, role => $role ); - printMessage("$role role was removed from authorizedGroup $authorizedGroupId and resource $resourceId", $batch); - } - #serviceId - } elsif (!defined($facilityId) && !defined($groupId) && !defined($voId) && !defined($resourceId) && defined($serviceId)) { - my $servicesAgent = $agent->getServicesAgent; - my $service = $servicesAgent->getServiceById( id => $serviceId ); - if (defined($userId)) { - $authzResolverAgent->unsetRole( user => $userId, complementaryObject => $service, role => $role ); - printMessage("$role role was removed from user $userId and service $serviceId", $batch); - } else { - $authzResolverAgent->unsetRole( authorizedGroup => $authorizedGroupId, complementaryObject => $service, role => $role ); - printMessage("$role role was removed from authorizedGroup $authorizedGroupId and service $serviceId", $batch); - } - #no object or more than one - } else { - die "ERROR: only one of objects (facility,vo,group,resource,service) id need to be set\n"; } } +die "ERROR: role $role not found\n"; + +sub getObject { + switch(lc $compObjectName) { + case "vo" { + my $vosAgent = $agent->getVosAgent; + return $vosAgent->getVoById( id => $objectId ); + } + case "facility" { + my $facilitiesAgent = $agent->getFacilitiesAgent; + return $facilitiesAgent->getFacilityById( id => $objectId ); + } + case "group" { + my $groupsAgent = $agent->getGroupsAgent; + return $groupsAgent->getGroupById( id => $objectId ); + } + case "resource" { + my $resourcesAgent = $agent->getResourcesAgent; + return $resourcesAgent->getResourceById( id => $objectId ); + } + case "service" { + my $servicesAgent = $agent->getServicesAgent; + return $servicesAgent->getServiceById( id => $objectId ); + } + case "securityteam" { + my $securityTeamsAgent = $agent->getSecurityTeamsAgent; + return $securityTeamsAgent->getSecurityTeamById( id => $objectId ); + } + else { + die "ERROR: Invalid object name\n"; + } + } +} \ No newline at end of file From f70b2d5bc74ee42d9c442ffca5d95966afdb901c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=BDuboslav=20Halama?= Date: Wed, 26 Oct 2022 17:12:52 +0200 Subject: [PATCH 3/3] feat(core): New service account role for SP reg application created * Role added into Role.java file * perun-roles file adjusted to contain newly created role (documentation, method call policies, role management settings) * new script ensuring adding all necessary policies into DB created --- .../cz/metacentrum/perun/core/api/Role.java | 3 +- perun-base/src/main/resources/perun-roles.yml | 45 +++++ perun-base/src/test/resources/test-roles.yml | 5 + .../core/blImpl/AttributesManagerBlImpl.java | 1 + .../api/AuthzResolverIntegrationTest.java | 15 ++ perun-db/addSPRegServiceAccountPolicies | 178 ++++++++++++++++++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100755 perun-db/addSPRegServiceAccountPolicies diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/Role.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/Role.java index 1926d284c9..d1ec3b3ea0 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/Role.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/Role.java @@ -24,6 +24,7 @@ public class Role { public static final String RPC = "RPC"; public static final String NOTIFICATIONS = "NOTIFICATIONS"; public static final String SERVICEUSER = "SERVICEUSER"; + public static final String SPREGAPPLICATION = "SPREGAPPLICATION"; public static final String SPONSOR = "SPONSOR"; public static final String VOOBSERVER = "VOOBSERVER"; public static final String TOPGROUPCREATOR = "TOPGROUPCREATOR"; @@ -38,7 +39,7 @@ public class Role { public static List rolesAsList() { return Arrays.asList(AUDITCONSUMERADMIN, CABINETADMIN, ENGINE, FACILITYADMIN, FACILITYOBSERVER, TRUSTEDFACILITYADMIN, GROUPADMIN, GROUPOBSERVER, GROUPMEMBERSHIPMANAGER, MEMBERSHIP, NOTIFICATIONS, PERUNADMIN, PERUNOBSERVER, REGISTRAR, RESOURCEADMIN, RESOURCEOBSERVER, - RESOURCESELFSERVICE, RPC, SECURITYADMIN, SELF, SERVICEUSER, SPONSOR, TOPGROUPCREATOR, UNKNOWNROLENAME, + RESOURCESELFSERVICE, RPC, SECURITYADMIN, SELF, SERVICEUSER, SPREGAPPLICATION, SPONSOR, TOPGROUPCREATOR, UNKNOWNROLENAME, VOADMIN, VOOBSERVER, SPONSORSHIP, MFA); } } diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 7d9ed870f2..cb17b0b757 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -75,6 +75,9 @@ # It is a sign of an account with a Self role being created by someone else. # The only role with privileges to create a Service user is the Perun administrator role. # +# SPREGAPPLICATION is a role that can be assigned to a service account primarily to work +# with facilities, groups and attributes. +# # SPONSOR users or groups of a VO can provide other users with VO membership even without them passing the VO registration. # The sponsor role may be assigned to users or groups of users by VO administrators or Perun administrators. # However, Sponsors are not allowed to delegate this role to other users or groups. @@ -124,6 +127,7 @@ perun_roles: - RPC - NOTIFICATIONS - SERVICEUSER + - SPREGAPPLICATION - SPONSOR - VOOBSERVER - TOPGROUPCREATOR @@ -592,6 +596,7 @@ perun_policies: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility - PERUNOBSERVER: + - SPREGAPPLICATION: include_policies: - default_policy @@ -668,6 +673,7 @@ perun_policies: - FACILITYADMIN: - FACILITYOBSERVER: - PERUNOBSERVER: + - SPREGAPPLICATION: include_policies: - default_policy @@ -846,12 +852,14 @@ perun_policies: createFacility_Facility_policy: policy_roles: - FACILITYADMIN: + - SPREGAPPLICATION: include_policies: - default_policy deleteFacility_Facility_Boolean_policy: policy_roles: - FACILITYADMIN: Facility + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -860,6 +868,7 @@ perun_policies: updateFacility_Facility_policy: policy_roles: - FACILITYADMIN: Facility + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1023,6 +1032,7 @@ perun_policies: - PERUNOBSERVER: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility + - SPREGAPPLICATION: include_policies: - default_policy @@ -1038,6 +1048,7 @@ perun_policies: policy_roles: - PERUNOBSERVER: - SELF: User + - SPREGAPPLICATION: include_policies: - default_policy @@ -1260,6 +1271,7 @@ perun_policies: policy_roles: - GROUPADMIN: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1270,6 +1282,7 @@ perun_policies: policy_roles: - GROUPADMIN: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1299,6 +1312,7 @@ perun_policies: policy_roles: - GROUPADMIN: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1440,6 +1454,7 @@ perun_policies: - GROUPADMIN: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1451,6 +1466,7 @@ perun_policies: - GROUPADMIN: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1463,6 +1479,7 @@ perun_policies: - GROUPADMIN: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1475,6 +1492,7 @@ perun_policies: - GROUPADMIN: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy mfa_rules: @@ -1490,6 +1508,7 @@ perun_policies: - GROUPOBSERVER: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy @@ -1534,6 +1553,7 @@ perun_policies: - GROUPOBSERVER: Group - GROUPMEMBERSHIPMANAGER: Group - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy @@ -2588,6 +2608,7 @@ perun_policies: - PERUNOBSERVER: - VOOBSERVER: Vo - VOADMIN: Vo + - SPREGAPPLICATION: include_policies: - default_policy @@ -4769,6 +4790,7 @@ perun_policies: getFacilities_Map_policy: policy_roles: - PERUNOBSERVER: + - SPREGAPPLICATION: include_policies: - default_policy @@ -5536,6 +5558,7 @@ perun_policies: - VOADMIN: - SELF: User - PERUNOBSERVER: + - SPREGAPPLICATION: include_policies: - default_policy @@ -5619,6 +5642,7 @@ perun_policies: - SELF: User - REGISTRAR: - PERUNOBSERVER: + - SPREGAPPLICATION: include_policies: - default_policy @@ -8005,11 +8029,13 @@ perun_roles_management: privileged_roles_to_manage: - PERUNADMIN: - FACILITYADMIN: Facility + - SPREGAPPLICATION: privileged_roles_to_read: - PERUNADMIN: - PERUNOBSERVER: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility + - SPREGAPPLICATION: associated_read_roles: - FACILITYOBSERVER assignable_to_attributes: true @@ -8027,11 +8053,13 @@ perun_roles_management: privileged_roles_to_manage: - PERUNADMIN: - FACILITYADMIN: Facility + - SPREGAPPLICATION: privileged_roles_to_read: - PERUNADMIN: - PERUNOBSERVER: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility + - SPREGAPPLICATION: associated_read_roles: [] assignable_to_attributes: false display_name: "Facility observer" @@ -8220,6 +8248,23 @@ perun_roles_management: assignable_to_attributes: false display_name: "Service user" + SPREGAPPLICATION: + primary_object: + assign_to_objects: {} + assignment_check: + - MFA: + entities_to_manage: + User: user_id + privileged_roles_to_manage: + - PERUNADMIN: + privileged_roles_to_read: + - PERUNADMIN: + - PERUNOBSERVER: + associated_read_roles: [] + assignable_to_attributes: true + skip_mfa: true + display_name: "SPREG application" + SPONSOR: primary_object: Vo assign_to_objects: diff --git a/perun-base/src/test/resources/test-roles.yml b/perun-base/src/test/resources/test-roles.yml index 9f4909609c..1a1bb8e721 100644 --- a/perun-base/src/test/resources/test-roles.yml +++ b/perun-base/src/test/resources/test-roles.yml @@ -176,5 +176,10 @@ perun_policies: - GROUPMEMBERSHIPMANAGER: Group include_policies: [] + test_spregapplication: + policy_roles: + - SPREGAPPLICATION: + include_policies: [] + perun_roles_management: {} ... \ No newline at end of file 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 0bc2b4cc08..0a0d71ed1a 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 @@ -7162,6 +7162,7 @@ protected void initialize() { policies.add(Triple.of(Role.GROUPADMIN, READ, RoleObject.Vo)); policies.add(Triple.of(Role.GROUPMEMBERSHIPMANAGER, READ, RoleObject.Vo)); policies.add(Triple.of(Role.FACILITYADMIN, READ, RoleObject.Facility)); + policies.add(Triple.of(Role.SPREGAPPLICATION, READ, RoleObject.None)); attributes.put(attr, createInitialPolicyCollections(policies)); //urn:perun:user:attribute-def:def:phone diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java index 481ccc2b8a..79d86fc65a 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java @@ -1833,6 +1833,21 @@ public void testGroupMembershipManager() throws Exception { assertTrue(AuthzResolver.authorizedInternal(session, "test_groupmembershipmanager", Arrays.asList(createdGroup))); } + @Test + public void testSpregApplication() throws Exception { + System.out.println(CLASS_NAME + "testSpregApplication"); + final Vo createdVo = perun.getVosManager().createVo(sess, new Vo(0, "SpregApplicationTestVo", "SpregApplicationTestVo")); + + final Member createdMember = createSomeMember(createdVo); + final User createdUser = perun.getUsersManagerBl().getUserByMember(sess, createdMember); + + AuthzResolver.setRole(sess, createdUser, null, Role.SPREGAPPLICATION); + + PerunSession session = getHisSession(createdMember); + AuthzResolver.refreshAuthz(session); + assertTrue(AuthzResolver.authorizedInternal(session, "test_spregapplication", Arrays.asList(createdVo))); + } + // private methods ============================================================== private Facility setUpFacility() throws Exception { diff --git a/perun-db/addSPRegServiceAccountPolicies b/perun-db/addSPRegServiceAccountPolicies new file mode 100755 index 0000000000..561ab86f49 --- /dev/null +++ b/perun-db/addSPRegServiceAccountPolicies @@ -0,0 +1,178 @@ +#!/usr/bin/perl -w +use strict; +use warnings FATAL => 'all'; + +use DBI; +use POSIX qw(:errno_h); +use Getopt::Long qw(:config no_ignore_case); + +sub help { + return qq{ + Adds SPREGAPPLICATION role to attribute policies. + SPREGAPPLICATION can read selected facility attributes and user attributes. + The script creates new policy collections for these attributes with single policy containing the new role. + If the role is not in the database, the script inserts it first. + -------------------------------------- + Available options: + + --user | -u Username for Perun DB (required) + --password | -w Password for Perun DB (required) + --overwrite | -o removes all existing SPREG application COLLECTIONS! + }; +} + +# Specific attributes besides READ for facility attributes +my @readAttributes = ( + "urn:perun:user:attribute-def:def:preferredMail" +); +my @readWriteAttributes = ( + "urn:perun:facility:attribute-def:def:proxyIdentifiers", + "urn:perun:facility:attribute-def:def:masterProxyIdentifier", + "urn:perun:facility:attribute-def:def:isTestSp", + "urn:perun:facility:attribute-def:def:showOnServiceList", + "urn:perun:facility:attribute-def:def:administratorContact", + "urn:perun:facility:attribute-def:def:OIDCClientID", + "urn:perun:facility:attribute-def:def:OIDCClientSecret", + "urn:perun:facility:attribute-def:def:entityID", + "urn:perun:facility:attribute-def:def:isSamlFacility", + "urn:perun:facility:attribute-def:def:isOidcFacility", + "urn:perun:facility:attribute-def:def:serviceName", + "urn:perun:facility:attribute-def:def:serviceDescription", + "urn:perun:facility:attribute-def:def:rpManagersGroupId", + "urn:perun:facility:attribute-def:def:spInformationURL", + "urn:perun:facility:attribute-def:def:rpLoginURL", + "urn:perun:facility:attribute-def:def:RaS", + "urn:perun:facility:attribute-def:def:spPrivacyPolicyURL", + "urn:perun:facility:attribute-def:def:spAdminContact", + "urn:perun:facility:attribute-def:def:spSupportContact", + "urn:perun:facility:attribute-def:def:spSecurityContact", + "urn:perun:facility:attribute-def:def:spInternal", + "urn:perun:facility:attribute-def:def:rpCategory", + "urn:perun:facility:attribute-def:def:organizationName", + "urn:perun:facility:attribute-def:def:spOrganizationURL", + "urn:perun:facility:attribute-def:def:CoCo", + "urn:perun:facility:attribute-def:def:assertionConsumerServices", + "urn:perun:facility:attribute-def:def:singleLogoutServices", + "urn:perun:facility:attribute-def:def:requiredAttributes", + "urn:perun:facility:attribute-def:def:signingCert", + "urn:perun:facility:attribute-def:def:encryptionCert", + "urn:perun:facility:attribute-def:def:metadataURL", + "urn:perun:facility:attribute-def:def:nameIDFormat", + "urn:perun:facility:attribute-def:def:relayState", + "urn:perun:facility:attribute-def:def:OIDCRedirectURIs", + "urn:perun:facility:attribute-def:def:OIDCFlowTypes", + "urn:perun:facility:attribute-def:def:OIDCCodeChallengeType", + "urn:perun:facility:attribute-def:def:OIDCIssuedRefreshTokens", + "urn:perun:facility:attribute-def:def:OIDCAllowIntrospection", + "urn:perun:facility:attribute-def:def:requiredScopes", + "urn:perun:facility:attribute-def:def:OIDCLogoutRedirectURIs", + "urn:perun:facility:attribute-def:def:checkGroupMembership", + "urn:perun:facility:attribute-def:def:allowRegistration", + "urn:perun:facility:attribute-def:def:dynamicRegistration", + "urn:perun:facility:attribute-def:def:registrationURL", + "urn:perun:facility:attribute-def:def:OIDCTokenEndpointAuthenticationMethod", + "urn:perun:facility:attribute-def:def:informationURL", + "urn:perun:facility:attribute-def:def:loginURL", + "urn:perun:facility:attribute-def:def:rpJurisdiction", + "urn:perun:facility:attribute-def:def:privacyPolicyURL", + "urn:perun:facility:attribute-def:def:organizationURL", + "urn:perun:facility:attribute-def:def:certData", + "urn:perun:facility:attribute-def:def:OIDCPostLogoutRedirectURIs", + "urn:perun:facility:attribute-def:def:rpInformationURL", + "urn:perun:facility:attribute-def:def:rpPrivacyPolicy", + "urn:perun:facility:attribute-def:def:rpAdminContact", + "urn:perun:facility:attribute-def:def:rpSupportContact", + "urn:perun:facility:attribute-def:def:rpServiceAccessTerms", + "urn:perun:facility:attribute-def:def:rpServiceIsNonEinfraUserEnabled", + "urn:perun:facility:attribute-def:def:rpServiceGrantTermsUrl", + "urn:perun:facility:attribute-def:def:rpOrganizationServiceGarant", + "urn:perun:facility:attribute-def:def:assertionConsumerService", + "urn:perun:facility:attribute-def:def:singleLogoutService", + "urn:perun:facility:attribute-def:def:OIDCIssueRefreshTokens" +); +my ($user, $pwd, $overwrite); + +GetOptions ("help|h" => sub { print help(); exit 0;}, + "user|u=s" => \$user, + "password|w=s" => \$pwd, + "overwrite|o" => \$overwrite) || die help(); + +if (!defined $user) { print "[ERROR] Username for Perun DB is required! Use --help | -h to print help.\n"; exit 1; } +if (!defined $pwd) { print "[ERROR] Password for Perun DB is required! Use --help | -h to print help.\n"; exit 1; } + +my $dbh = DBI->connect('dbi:Pg:dbname=perun',$user,$pwd,{RaiseError=>1,AutoCommit=>0,pg_enable_utf8=>1}) or die EPERM," Connect"; + +my $stGetAttributeId = q{select a.id from attr_names a where a.attr_name=?}; + +my $sthNewCollection = $dbh->prepare(q{insert into attribute_policy_collections (id, attr_id, action) values (?,?,?);}); + +my $sthNewPolicy = $dbh->prepare(q{insert into attribute_policies (id, role_id, object, policy_collection_id) values (?,?,'None',?);}); + +my $sthInsertRole = $dbh->prepare(q{insert into roles (id, name) values (?,'spregapplication');}); + +my $sthDelCollections = $dbh->prepare(q{delete from attribute_policy_collections where id=?;}); + +my $sthExistingPolicies = $dbh -> prepare(q{select policy_collection_id from attribute_policies where role_id=?;}); + +# ******************************* +# ** E X E C U T I O N ** +# ******************************* + +# Insert SPREGAPPLICATION role to DB if not created yet +my $roleId = $dbh->selectrow_array(q{select id from roles where name='spregapplication'}); +unless ($roleId) { + $roleId = $dbh->selectrow_array('select nextval(\'roles_id_seq\')'); + $sthInsertRole->execute($roleId); + $dbh->commit() or die $dbh->errstr; + print "INFO: Spregapplication role created\n"; +} + +# Remove all collections where any policy with SPREGAPPLICATION exists +if ($overwrite) { + $sthExistingPolicies->execute($roleId); + while (my @row = $sthExistingPolicies->fetchrow_array()) { + my ($collectionId) = @row; + $sthDelCollections->execute($collectionId); + } + $dbh->commit() or die $dbh->errstr; + print "INFO: Existing policy collections successfully removed.\n"; +} + +# set WRITE policies for chosen attributes for SPREGAPPLICATION +foreach my $attrName (@readWriteAttributes) { + my $attrId = $dbh->selectrow_array($stGetAttributeId, {}, $attrName); + + # If attribute does not exist on given instance, skip it + unless ($attrId) { + next; + } + + my $nextCollectionId = $dbh->selectrow_array('select nextval(\'attribute_policy_collections_id_seq\')'); + my $nextPolicyId = $dbh->selectrow_array('select nextval(\'attribute_policies_id_seq\')'); + $sthNewCollection->execute($nextCollectionId, $attrId, "WRITE"); #(id, attr_id, action) + $sthNewPolicy->execute($nextPolicyId, $roleId, $nextCollectionId); #(id, role_id, policy_collection_id) +} + +# add READ/WRITE attributes to READ attributes array +push(@readAttributes, @readWriteAttributes); + +# set policies for READ attributes for SPREGAPPLICATION +foreach my $attrName (@readAttributes) { + my $attrId = $dbh->selectrow_array($stGetAttributeId, {}, $attrName); + + # If attribute does not exist on given instance, skip it + unless ($attrId) { + next; + } + + my $nextCollectionId = $dbh->selectrow_array('select nextval(\'attribute_policy_collections_id_seq\')'); + my $nextPolicyId = $dbh->selectrow_array('select nextval(\'attribute_policies_id_seq\')'); + $sthNewCollection->execute($nextCollectionId, $attrId, "READ"); #(id, attr_id, action) + $sthNewPolicy->execute($nextPolicyId, $roleId, $nextCollectionId); #(id, role_id, policy_collection_id) +} + +$dbh->commit() or die $dbh->errstr; + +print "INFO: Spregapplication role successfully set.\n"; +print "===================================================\n"; +$dbh->disconnect() or die $dbh->errstr; \ No newline at end of file