Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • dBucik/endpoint_auth_methods
  • dBucik/skip_logout_confirm
  • fix_juridiction
  • main
  • revert-d2e009d2
  • token_endpoint_auth_methods
  • mitreid-connect-0.9.0
  • mitreid-connect-0.9.1
  • mitreid-connect-0.9.2
  • mitreid-connect-0.9.3
  • mitreid-connect-1.0.0
  • mitreid-connect-1.0.1
  • mitreid-connect-1.0.10
  • mitreid-connect-1.0.11
  • mitreid-connect-1.0.12
  • mitreid-connect-1.0.13
  • mitreid-connect-1.0.14
  • mitreid-connect-1.0.15
  • mitreid-connect-1.0.16
  • mitreid-connect-1.0.17
  • mitreid-connect-1.0.18
  • mitreid-connect-1.0.19
  • mitreid-connect-1.0.2
  • mitreid-connect-1.0.20
  • mitreid-connect-1.0.21
  • mitreid-connect-1.0.22
  • mitreid-connect-1.0.3
  • mitreid-connect-1.0.4
  • mitreid-connect-1.0.5
  • mitreid-connect-1.0.6
  • mitreid-connect-1.0.7
  • mitreid-connect-1.0.8
  • mitreid-connect-1.0.9
  • mitreid-connect-1.1.0
  • mitreid-connect-1.1.1
  • mitreid-connect-1.1.10
  • mitreid-connect-1.1.11
  • mitreid-connect-1.1.12
  • mitreid-connect-1.1.13
  • mitreid-connect-1.1.14
  • mitreid-connect-1.1.15
  • mitreid-connect-1.1.16
  • mitreid-connect-1.1.17
  • mitreid-connect-1.1.18
  • mitreid-connect-1.1.19
  • mitreid-connect-1.1.2
  • mitreid-connect-1.1.3
  • mitreid-connect-1.1.4
  • mitreid-connect-1.1.5
  • mitreid-connect-1.1.6
  • mitreid-connect-1.1.7
  • mitreid-connect-1.1.8
  • mitreid-connect-1.1.9
  • mitreid-connect-1.2.0
  • mitreid-connect-1.2.0-RC1
  • mitreid-connect-1.2.0-RC2
  • mitreid-connect-1.2.1
  • mitreid-connect-1.2.2
  • mitreid-connect-1.2.3
  • mitreid-connect-1.2.4
  • mitreid-connect-1.2.5
  • mitreid-connect-1.2.6
  • mitreid-connect-1.3.0
  • mitreid-connect-1.3.0-RC1
  • mitreid-connect-1.3.0-RC2
  • mitreid-connect-1.3.1
  • mitreid-connect-1.3.2
  • mitreid-connect-1.3.3
  • v10.0.0
  • v10.0.1
  • v10.0.2
  • v10.0.3
  • v10.1.0
  • v10.1.1
  • v10.1.2
  • v10.1.3
  • v10.2.0
  • v10.2.1
  • v10.3.0
  • v10.3.1
  • v10.3.2
  • v10.3.3
  • v10.3.4
  • v10.4.0
  • v10.5.0
  • v10.5.1
  • v10.5.2
  • v10.5.3
  • v10.5.4
  • v11.0.0
  • v11.0.1
  • v11.0.2
  • v11.0.3
  • v12.0.0
  • v12.0.1
  • v12.0.2
  • v12.0.3
  • v12.0.4
  • v12.0.5
  • v12.1.0
  • v12.1.1
  • v12.1.2
  • v12.1.3
  • v12.1.4
  • v12.1.5
  • v12.1.6
106 results

Target

Select target project
  • perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server
1 result
Select Git revision
  • dBucik/endpoint_auth_methods
  • dBucik/skip_logout_confirm
  • fix_juridiction
  • main
  • revert-d2e009d2
  • token_endpoint_auth_methods
  • mitreid-connect-0.9.0
  • mitreid-connect-0.9.1
  • mitreid-connect-0.9.2
  • mitreid-connect-0.9.3
  • mitreid-connect-1.0.0
  • mitreid-connect-1.0.1
  • mitreid-connect-1.0.10
  • mitreid-connect-1.0.11
  • mitreid-connect-1.0.12
  • mitreid-connect-1.0.13
  • mitreid-connect-1.0.14
  • mitreid-connect-1.0.15
  • mitreid-connect-1.0.16
  • mitreid-connect-1.0.17
  • mitreid-connect-1.0.18
  • mitreid-connect-1.0.19
  • mitreid-connect-1.0.2
  • mitreid-connect-1.0.20
  • mitreid-connect-1.0.21
  • mitreid-connect-1.0.22
  • mitreid-connect-1.0.3
  • mitreid-connect-1.0.4
  • mitreid-connect-1.0.5
  • mitreid-connect-1.0.6
  • mitreid-connect-1.0.7
  • mitreid-connect-1.0.8
  • mitreid-connect-1.0.9
  • mitreid-connect-1.1.0
  • mitreid-connect-1.1.1
  • mitreid-connect-1.1.10
  • mitreid-connect-1.1.11
  • mitreid-connect-1.1.12
  • mitreid-connect-1.1.13
  • mitreid-connect-1.1.14
  • mitreid-connect-1.1.15
  • mitreid-connect-1.1.16
  • mitreid-connect-1.1.17
  • mitreid-connect-1.1.18
  • mitreid-connect-1.1.19
  • mitreid-connect-1.1.2
  • mitreid-connect-1.1.3
  • mitreid-connect-1.1.4
  • mitreid-connect-1.1.5
  • mitreid-connect-1.1.6
  • mitreid-connect-1.1.7
  • mitreid-connect-1.1.8
  • mitreid-connect-1.1.9
  • mitreid-connect-1.2.0
  • mitreid-connect-1.2.0-RC1
  • mitreid-connect-1.2.0-RC2
  • mitreid-connect-1.2.1
  • mitreid-connect-1.2.2
  • mitreid-connect-1.2.3
  • mitreid-connect-1.2.4
  • mitreid-connect-1.2.5
  • mitreid-connect-1.2.6
  • mitreid-connect-1.3.0
  • mitreid-connect-1.3.0-RC1
  • mitreid-connect-1.3.0-RC2
  • mitreid-connect-1.3.1
  • mitreid-connect-1.3.2
  • mitreid-connect-1.3.3
  • v10.0.0
  • v10.0.1
  • v10.0.2
  • v10.0.3
  • v10.1.0
  • v10.1.1
  • v10.1.2
  • v10.1.3
  • v10.2.0
  • v10.2.1
  • v10.3.0
  • v10.3.1
  • v10.3.2
  • v10.3.3
  • v10.3.4
  • v10.4.0
  • v10.5.0
  • v10.5.1
  • v10.5.2
  • v10.5.3
  • v10.5.4
  • v11.0.0
  • v11.0.1
  • v11.0.2
  • v11.0.3
  • v12.0.0
  • v12.0.1
  • v12.0.2
  • v12.0.3
  • v12.0.4
  • v12.0.5
  • v12.1.0
  • v12.1.1
  • v12.1.2
  • v12.1.3
  • v12.1.4
  • v12.1.5
  • v12.1.6
106 results
Show changes
Commits on Source (8)
Showing
with 1880 additions and 1561 deletions
# [18.5.0](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/compare/v18.4.0...v18.5.0) (2024-05-15)
### Features
* 🎸 filtering of assigned resource groups in entitlements ([7f1cf4c](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/commit/7f1cf4c531fe5b7dd5069bcb4645f35dbc8662f4))
* 🎸 filtering of groups in access control filter ([76ce690](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/commit/76ce690363dbe25097836f609ab6f6aca2197b5b))
# [18.4.0](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/compare/v18.3.1...v18.4.0) (2024-04-23)
......
# MITREid Connect
![maintenance status: end of life](https://img.shields.io/maintenance/end%20of%20life/2023)
![maintenance status: end of life](https://img.shields.io/maintenance/end%20of%20life/2024)
This project has reached end of life, which means no new features will be added. Security patches and important bug fixes will end as of 2023. Check out [SATOSA](https://github.com/IdentityPython/SATOSA) and its [OIDC frontend](https://github.com/UniversitaDellaCalabria/SATOSA-oidcop) instead.
This project has reached end of life, which means no new features will be added. Security patches and important bug fixes will end as of 2024. Check out [Apereo CAS](https://apereo.github.io/cas/) instead.
## Description
......
......@@ -21,7 +21,7 @@
<parent>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>18.4.0</version>
<version>18.5.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -22,7 +22,7 @@
<parent>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>18.4.0</version>
<version>18.5.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -12,8 +12,8 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import static cz.muni.ics.oidc.server.filters.AuthProcFilterConstants.PARAM_POST_LOGOUT_REDIRECT_URI;
import static cz.muni.ics.oidc.server.filters.AuthProcFilterConstants.PARAM_STATE;
import static cz.muni.ics.openid.connect.web.endpoint.EndSessionEndpoint.PARAM_POST_LOGOUT_REDIRECT_URI;
@Slf4j
public class PerunOidcLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
......
......@@ -86,11 +86,12 @@ public interface PerunAdapterMethods {
* Perform check if user can access service based on his/her membership
* in groups assigned to facility resources
*
* @param facility Facility object
* @param userId ID of user
* @param facility Facility object
* @param userId ID of user
* @param accessControlDisabledAttr
* @return TRUE if user can access, FALSE otherwise
*/
boolean canUserAccessBasedOnMembership(Facility facility, Long userId);
boolean canUserAccessBasedOnMembership(Facility facility, Long userId, String accessControlDisabledAttr);
/**
* Fetch facility attribute values
......@@ -156,11 +157,16 @@ public interface PerunAdapterMethods {
/**
* Get groups where user is active (also in VO in which group exists) and are assigned to the resources of facility.
* Fill the uniqueGroupName for groups as well.
* @param facilityId Id of Facility
* @param userId Id of User
*
* @param facilityId Id of Facility
* @param userId Id of User
* @param resourceGroupEntitlementDisabledAttribute Attribute to check if groups assigned to resource should be
* considered. Pass null to ignore the setting.
* @return Set of groups (filled or empty)
*/
Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId, Long userId);
Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId,
Long userId,
String resourceGroupEntitlementDisabledAttribute);
/**
* Fetch VO attribute values
......
......@@ -62,12 +62,12 @@ public class PerunAdapterImpl extends PerunAdapter {
}
@Override
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId) {
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId, String accessControlDisabledAttr) {
try {
return this.getAdapterPrimary().canUserAccessBasedOnMembership(facility, userId);
return this.getAdapterPrimary().canUserAccessBasedOnMembership(facility, userId, accessControlDisabledAttr);
} catch (UnsupportedOperationException e) {
if (this.isCallFallback()) {
return this.getAdapterFallback().canUserAccessBasedOnMembership(facility, userId);
return this.getAdapterFallback().canUserAccessBasedOnMembership(facility, userId, accessControlDisabledAttr);
} else {
throw e;
}
......@@ -334,12 +334,17 @@ public class PerunAdapterImpl extends PerunAdapter {
}
@Override
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId, Long userId) {
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId,
Long userId,
String resourceGroupEntitlementDisabledAttribute)
{
try {
return this.getAdapterPrimary().getGroupsWhereUserIsActiveWithUniqueNames(facilityId, userId);
return this.getAdapterPrimary().getGroupsWhereUserIsActiveWithUniqueNames(
facilityId, userId, resourceGroupEntitlementDisabledAttribute);
} catch (UnsupportedOperationException e) {
if (this.isCallFallback()) {
return this.getAdapterFallback().getGroupsWhereUserIsActiveWithUniqueNames(facilityId, userId);
return this.getAdapterFallback().getGroupsWhereUserIsActiveWithUniqueNames(
facilityId, userId, resourceGroupEntitlementDisabledAttribute);
} else {
throw e;
}
......
......@@ -142,14 +142,14 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
}
@Override
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId) {
Set<Long> groupsWithAccessIds = getGroupIdsWithAccessToFacility(facility.getId());
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId, String accessControlDisabledAttr) {
Set<Long> groupsWithAccessIds = getGroupIdsAssignedToFacility(facility.getId(), accessControlDisabledAttr);
if (groupsWithAccessIds == null || groupsWithAccessIds.isEmpty()) {
return false;
}
Set<Long> userGroupIds = getGroupIdsWhereUserIsMember(userId, null);
if (userGroupIds == null || userGroupIds.isEmpty()) {
if (userGroupIds.isEmpty()) {
return false;
}
......@@ -212,7 +212,7 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
public List<String> getGroupsAssignedToResourcesWithUniqueNames(Facility facility) {
List<String> res = new ArrayList<>();
Set<Long> groupIds = getGroupIdsWithAccessToFacility(facility.getId());
Set<Long> groupIds = getGroupIdsAssignedToFacility(facility.getId(), null);
if (groupIds == null || groupIds.isEmpty()) {
return res;
}
......@@ -415,9 +415,13 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
}
@Override
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId, Long userId) {
Set<Long> userGroups = this.getGroupIdsWhereUserIsMember(userId, null);
Set<Long> facilityGroups = this.getGroupIdsWithAccessToFacility(facilityId);
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId,
Long userId,
String resourceGroupEntitlementDisabledAttribute)
{
Set<Long> userGroups = getGroupIdsWhereUserIsMember(userId, null);
Set<Long> facilityGroups = getGroupIdsAssignedToFacility(
facilityId, resourceGroupEntitlementDisabledAttribute);
Set<Long> groupIds = userGroups.stream()
.filter(facilityGroups::contains)
.collect(Collectors.toSet());
......@@ -659,15 +663,50 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
return PERUN_USER_ID + '=' + userId + ',' + OU_PEOPLE;
}
private Set<Long> getGroupIdsWithAccessToFacility(Long facilityId) {
private Set<Long> getGroupIdsAssignedToFacility(Long facilityId, String ignoreGroupsAttribute) {
String ldapResourceGroupEntitlementDisabledAttrName = null;
if (ignoreGroupsAttribute != null) {
AttributeMapping mapping = getMappingForAttrName(PerunEntityType.RESOURCE, ignoreGroupsAttribute);
if (mapping == null) {
log.warn("No Attribute mapping found for '{}'", ignoreGroupsAttribute);
} else {
ldapResourceGroupEntitlementDisabledAttrName = mapping.getLdapName();
}
}
final String ignoreResourceAttr = ldapResourceGroupEntitlementDisabledAttrName;
FilterBuilder filter = and(equal(OBJECT_CLASS, PERUN_RESOURCE), equal(PERUN_FACILITY_ID, String.valueOf(facilityId)));
String[] attributes = new String[] { ASSIGNED_GROUP_ID };
Set<String> attributesToFetch = new HashSet<>();
attributesToFetch.add(ASSIGNED_GROUP_ID);
final String[] mandatoryAttributes = attributesToFetch.toArray(new String[] {});
if (StringUtils.hasText(ignoreResourceAttr)) {
attributesToFetch.add(ignoreResourceAttr);
}
final String[] attributes = attributesToFetch.toArray(new String[] {});
EntryMapper<Set<Long>> mapper = e -> {
Set<Long> ids = new HashSet<>();
if (checkHasAttributes(e, attributes)) {
if (checkHasAttributes(e, mandatoryAttributes)) {
boolean ignore = false;
if (StringUtils.hasText(ignoreResourceAttr)) {
Attribute ignoreAttr = e.get(ignoreResourceAttr);
if (ignoreAttr != null && "true".equalsIgnoreCase(ignoreAttr.getString())) {
ignore = true;
}
}
Attribute a = e.get(ASSIGNED_GROUP_ID);
if (a != null) {
a.iterator().forEachRemaining(id -> ids.add(Long.valueOf(id.getString())));
final Set<Long> groupIds = new HashSet<>();
a.iterator().forEachRemaining(id -> groupIds.add(Long.valueOf(id.getString())));
if (!ignore) {
log.debug("Including groups '{}' due to '{}' not set on a resource", groupIds, ignoreGroupsAttribute);
ids.addAll(groupIds);
} else {
log.debug("Ignoring groups '{}' due to '{}' set on a resource", groupIds, ignoreGroupsAttribute);
}
}
}
......@@ -844,6 +883,33 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
return mappings;
}
private AttributeMapping getMappingForAttrName(PerunEntityType entity, String attrToFetch) {
AttributeMapping mapping;
switch (entity) {
case USER:
mapping = getUserAttributesMappingService().getMappingByIdentifier(attrToFetch);
break;
case FACILITY:
mapping = getFacilityAttributesMappingService().getMappingByIdentifier(attrToFetch);
break;
case VO:
mapping = getVoAttributesMappingService().getMappingByIdentifier(attrToFetch);
break;
case GROUP:
mapping = getGroupAttributesMappingService().getMappingByIdentifier(attrToFetch);
break;
case RESOURCE:
mapping = getResourceAttributesMappingService().getMappingByIdentifier(attrToFetch);
break;
default:
log.error("Unknown ENTITY {}", entity);
mapping = null;
break;
}
return mapping;
}
private String[] getAttributesFromMappings(Set<AttributeMapping> mappings) {
return mappings.stream()
.map(AttributeMapping::getLdapName)
......
......@@ -37,11 +37,54 @@ import java.util.Set;
import java.util.stream.Collectors;
import static cz.muni.ics.oidc.models.PerunAttributeValue.STRING_TYPE;
import static cz.muni.ics.oidc.models.PerunAttributeValueAwareModel.ARRAY_TYPE;
import static cz.muni.ics.oidc.models.enums.MemberStatus.VALID;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.ATTRIBUTES_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.EXT_SOURCE_IDP;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.FACILITIES_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.GROUPS_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.MEMBERS_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ALLOWED_GROUPS;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ALLOWED_RESOURCES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_APPLICATION_FORM;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ASSIGNED_GROUPS;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ASSIGNED_RESOURCES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ASSIGNED_RICH_RESOURCES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ATTRIBUTE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ATTRIBUTES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ENTITYLESS_ATTRIBUTES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_ENTITYLESS_KEYS;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_FACILITIES_BY_ATTRIBUTE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_GROUPS_WHERE_USER_IS_ACTIVE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_GROUP_BY_ID;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_MEMBERS_BY_USER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_MEMBER_BY_USER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_MEMBER_GROUPS;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_RICH_GROUPS_ASSIGNED_TO_RESOURCE_WITH_ATTRIBUTES_BY_NAMES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_USER_BY_EXT_SOURCE_NAME_AND_EXT_LOGIN;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_USER_BY_ID;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_USER_EXT_SOURCES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_VO_BY_ID;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_GET_VO_BY_SHORT_NAME;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_IS_GROUP_MEMBER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.METHOD_SET_ATTRIBUTE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTRIBUTE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTRIBUTE_DEFINITION;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTRIBUTE_NAME;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTRIBUTE_VALUE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTR_NAME;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ATTR_NAMES;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_EXT_LOGIN;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_EXT_SOURCE_NAME;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_FACILITY;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_GROUP;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_ID;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_MEMBER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_RESOURCE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_SHORT_NAME;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_USER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_USER_EXT_SOURCE;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.PARAM_VO;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.REGISTRAR_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.RESOURCES_MANAGER;
import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.USERS_MANAGER;
......@@ -55,1295 +98,1371 @@ import static cz.muni.ics.oidc.server.connectors.PerunConnectorRpc.VOS_MANAGER;
* @author Peter Jancus jancus@ics.muni.cz
*/
@Slf4j
public class PerunAdapterRpc extends PerunAdapterWithMappingServices implements PerunAdapterMethods, PerunAdapterMethodsRpc {
private PerunConnectorRpc connectorRpc;
private String oidcClientIdAttr;
private String oidcCheckMembershipAttr;
private String orgUrlAttr;
private String affiliationsAttr;
public void setConnectorRpc(PerunConnectorRpc connectorRpc) {
this.connectorRpc = connectorRpc;
}
public void setOidcClientIdAttr(String oidcClientIdAttr) {
this.oidcClientIdAttr = oidcClientIdAttr;
}
public void setOidcCheckMembershipAttr(String oidcCheckMembershipAttr) {
this.oidcCheckMembershipAttr = oidcCheckMembershipAttr;
}
public void setOrgUrlAttr(String orgUrlAttr) {
this.orgUrlAttr = orgUrlAttr;
}
public void setAffiliationsAttr(String affiliationsAttr) {
this.affiliationsAttr = affiliationsAttr;
}
@Override
public PerunUser getPreauthenticatedUserId(String extLogin, String extSourceName) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("extLogin", extLogin);
map.put("extSourceName", extSourceName);
JsonNode response = connectorRpc.post(USERS_MANAGER, "getUserByExtSourceNameAndExtLogin", map);
return RpcMapper.mapPerunUser(response);
}
@Override
public Facility getFacilityByClientId(String clientId) {
if (!this.connectorRpc.isEnabled()) {
return null;
} else if (!StringUtils.hasText(clientId)) {
return null;
}
AttributeMapping mapping = this.getFacilityAttributesMappingService().getMappingByIdentifier(oidcClientIdAttr);
Map<String, Object> map = new LinkedHashMap<>();
map.put("attributeName", mapping.getRpcName());
map.put("attributeValue", clientId);
JsonNode jsonNode = connectorRpc.post(FACILITIES_MANAGER, "getFacilitiesByAttribute", map);
return (jsonNode.size() > 0) ? RpcMapper.mapFacility(jsonNode.get(0)) : null;
}
@Override
public boolean isMembershipCheckEnabledOnFacility(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
AttributeMapping mapping = this.getFacilityAttributesMappingService().getMappingByIdentifier(oidcCheckMembershipAttr);
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facility.getId());
map.put("attributeName", mapping.getRpcName());
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, "getAttribute", map);
return res.get("value").asBoolean(false);
}
@Override
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId) {
if (!this.connectorRpc.isEnabled()) {
return true;
}
List<Group> activeGroups = getGroupsWhereUserIsActive(facility, userId);
return !activeGroups.isEmpty();
}
@Override
public Map<Vo, List<Group>> getGroupsForRegistration(Facility facility, Long userId, List<String> voShortNames) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
List<Vo> vos = getVosByShortNames(voShortNames);
Map<Long, Vo> vosMap = convertVoListToMap(vos);
List<Member> userMembers = getMembersByUser(userId);
userMembers = new ArrayList<>(new HashSet<>(userMembers));
//Filter out vos where member is other than valid or expired. These vos cannot be used for registration
Map<Long, MemberStatus> memberVoStatuses = convertMembersListToStatusesMap(userMembers);
Map<Long, Vo> vosForRegistration = new HashMap<>();
for (Map.Entry<Long, Vo> entry : vosMap.entrySet()) {
if (memberVoStatuses.containsKey(entry.getKey())) {
MemberStatus status = memberVoStatuses.get(entry.getKey());
if (VALID.equals(status) || MemberStatus.EXPIRED.equals(status)) {
vosForRegistration.put(entry.getKey(), entry.getValue());
}
} else {
vosForRegistration.put(entry.getKey(), entry.getValue());
}
}
// filter groups only if their VO is in the allowed VOs and if they have registration form
List<Group> allowedGroups = getAllowedGroups(facility);
List<Group> groupsForRegistration = allowedGroups.stream()
.filter(group -> vosForRegistration.containsKey(group.getVoId()) && hasApplicationForm(group))
.collect(Collectors.toList());
// create map for processing
Map<Vo, List<Group>> result = new HashMap<>();
for (Group group : groupsForRegistration) {
Vo vo = vosMap.get(group.getVoId());
result.computeIfAbsent(vo, v -> new ArrayList<>());
result.get(vo).add(group);
}
return result;
}
@Override
public boolean groupWhereCanRegisterExists(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
List<Group> allowedGroups = getAllowedGroups(facility);
if (!allowedGroups.isEmpty()) {
for (Group group : allowedGroups) {
if (hasApplicationForm(group)) {
return true;
}
}
}
return false;
}
@Override
public boolean isUserInGroup(Long userId, Long groupId) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> groupParams = new LinkedHashMap<>();
groupParams.put("id", groupId);
JsonNode groupResponse = connectorRpc.post(GROUPS_MANAGER, "getGroupById", groupParams);
Group group = RpcMapper.mapGroup(groupResponse);
Map<String, Object> memberParams = new LinkedHashMap<>();
memberParams.put("vo", group.getVoId());
memberParams.put("user", userId);
JsonNode memberResponse = connectorRpc.post(MEMBERS_MANAGER, "getMemberByUser", memberParams);
Member member = RpcMapper.mapMember(memberResponse);
Map<String, Object> isGroupMemberParams = new LinkedHashMap<>();
isGroupMemberParams.put("group", groupId);
isGroupMemberParams.put("member", member.getId());
JsonNode res = connectorRpc.post(GROUPS_MANAGER, "isGroupMember", isGroupMemberParams);
return res.asBoolean(false);
}
@Override
public boolean setUserAttribute(Long userId, PerunAttribute attribute) {
if (!this.connectorRpc.isEnabled()) {
return true;
}
JsonNode attributeJson = attribute.toJson();
Map<String, Object> map = new LinkedHashMap<>();
map.put("user", userId);
map.put("attribute", attributeJson);
JsonNode response = connectorRpc.post(ATTRIBUTES_MANAGER, "setAttribute", map);
return (response == null || response.isNull() || response instanceof NullNode);
}
@Override
public List<Affiliation> getUserExtSourcesAffiliations(Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<UserExtSource> userExtSources = getUserExtSources(userId);
List<Affiliation> affiliations = new ArrayList<>();
AttributeMapping affMapping = new AttributeMapping("affMapping", affiliationsAttr, "", PerunAttributeValue.ARRAY_TYPE);
AttributeMapping orgUrlMapping = new AttributeMapping("orgUrl", orgUrlAttr, "", STRING_TYPE);
Set<AttributeMapping> attributeMappings = new HashSet<>(Arrays.asList(affMapping, orgUrlMapping));
for (UserExtSource ues : userExtSources) {
if ("cz.metacentrum.perun.core.impl.ExtSourceIdp".equals(ues.getExtSource().getType())) {
Map<String, PerunAttributeValue> uesAttrValues = getUserExtSourceAttributeValues(ues.getId(), attributeMappings);
long asserted = ues.getLastAccess().getTime() / 1000L;
String orgUrl = uesAttrValues.get(orgUrlMapping.getIdentifier()).valueAsString();
String affs = uesAttrValues.get(affMapping.getIdentifier()).valueAsString();
if (affs != null) {
for (String aff : affs.split(";")) {
String source = ( (orgUrl != null) ? orgUrl : ues.getExtSource().getName() );
Affiliation affiliation = new Affiliation(source, aff, asserted);
log.debug("found {} from IdP {} with orgURL {} asserted at {}", aff, ues.getExtSource().getName(),
orgUrl, asserted);
affiliations.add(affiliation);
}
}
}
}
return affiliations;
}
@Override
public List<Affiliation> getGroupAffiliations(Long userId, String groupAffiliationsAttr) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Affiliation> affiliations = new ArrayList<>();
List<Member> userMembers = getMembersByUser(userId);
for (Member member : userMembers) {
if (VALID.equals(member.getStatus())) {
List<Group> memberGroups = getMemberGroups(member.getId());
for (Group group : memberGroups) {
PerunAttributeValue attrValue = this.getGroupAttributeValue(group, groupAffiliationsAttr);
if (attrValue != null && attrValue.valueAsString() != null) {
long linuxTime = System.currentTimeMillis() / 1000L;
for (String value : attrValue.valueAsList()) {
Affiliation affiliation = new Affiliation(null, value, linuxTime);
log.debug("found {} on group {}", value, group.getName());
affiliations.add(affiliation);
}
}
}
}
}
return affiliations;
}
@Override
public List<String> getGroupsAssignedToResourcesWithUniqueNames(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Resource> resources = getAssignedResources(facility);
List<String> result = new ArrayList<>();
String voShortName = "urn:perun:group:attribute-def:virt:voShortName";
for (Resource res : resources) {
List<Group> groups = getRichGroupsAssignedToResourceWithAttributesByNames(res, Collections.singletonList(voShortName));
for (Group group : groups) {
if (group.getAttributeByUrnName(voShortName) != null &&
group.getAttributeByUrnName(voShortName).hasNonNull("value")) {
String value = group.getAttributeByUrnName(voShortName).get("value").textValue();
group.setUniqueGroupName(value + ":" + group.getName());
result.add(group.getUniqueGroupName());
}
}
}
return result;
}
@Override
public Map<String, PerunAttribute> getEntitylessAttributes(String attributeName) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, Object> attrNameMap = new LinkedHashMap<>();
attrNameMap.put("attrName", attributeName);
JsonNode entitylessAttributesJson = connectorRpc.post(ATTRIBUTES_MANAGER, "getEntitylessAttributes", attrNameMap);
Long attributeDefinitionId = RpcMapper.mapAttribute(entitylessAttributesJson.get(0)).getId();
Map<String, Object> attributeDefinitionIdMap = new LinkedHashMap<>();
attributeDefinitionIdMap.put("attributeDefinition", attributeDefinitionId);
JsonNode entitylessKeysJson = connectorRpc.post(ATTRIBUTES_MANAGER, "getEntitylessKeys", attributeDefinitionIdMap);
Map<String, PerunAttribute> result = new LinkedHashMap<>();
for(int i = 0; i < entitylessKeysJson.size(); i++) {
result.put(entitylessKeysJson.get(i).asText(), RpcMapper.mapAttribute(entitylessAttributesJson.get(i)));
}
if (result.size() == 0) {
return null;
}
return result;
}
@Override
public Vo getVoByShortName(String shortName) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("shortName", shortName);
JsonNode jsonNode = connectorRpc.post(VOS_MANAGER, "getVoByShortName", params);
return RpcMapper.mapVo(jsonNode);
}
@Override
public Map<String, PerunAttributeValue> getUserAttributeValues(PerunUser user, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getUserAttributeValues(user.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getUserAttributeValues(Long userId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> userAttributes = this.getUserAttributes(userId, attrsToFetch);
return extractValues(userAttributes);
}
@Override
public PerunAttributeValue getUserAttributeValue(PerunUser user, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getUserAttributeValue(user.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getUserAttributeValue(Long userId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getUserAttribute(userId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getFacilityAttributeValues(Facility facility, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getFacilityAttributeValues(facility.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getFacilityAttributeValues(Long facilityId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> facilityAttributes = this.getFacilityAttributes(facilityId, attrsToFetch);
return extractValues(facilityAttributes);
}
@Override
public PerunAttributeValue getFacilityAttributeValue(Facility facility, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getFacilityAttributeValue(facility.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getFacilityAttributeValue(Long facilityId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getFacilityAttribute(facilityId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getVoAttributeValues(Vo vo, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getVoAttributeValues(vo.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getVoAttributeValues(Long voId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> voAttributes = this.getVoAttributes(voId, attrsToFetch);
return extractValues(voAttributes);
}
@Override
public PerunAttributeValue getVoAttributeValue(Vo vo, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getVoAttributeValue(vo.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getVoAttributeValue(Long voId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getVoAttribute(voId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getGroupAttributeValues(Group group, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getGroupAttributeValues(group.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getGroupAttributeValues(Long groupId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> groupAttributes = this.getGroupAttributes(groupId, attrsToFetch);
return extractValues(groupAttributes);
}
@Override
public PerunAttributeValue getGroupAttributeValue(Group group, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getGroupAttributeValue(group.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getGroupAttributeValue(Long groupId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getGroupAttribute(groupId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getResourceAttributeValues(Resource resource, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getResourceAttributeValues(resource.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getResourceAttributeValues(Long resourceId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> resourceAttributes = this.getResourceAttributes(resourceId, attrsToFetch);
return extractValues(resourceAttributes);
}
@Override
public PerunAttributeValue getResourceAttributeValue(Resource resource, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getResourceAttributeValue(resource.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getResourceAttributeValue(Long resourceId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getResourceAttribute(resourceId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttribute> getFacilityAttributes(Facility facility, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getFacilityAttributes(facility.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getFacilityAttributes(Long facilityId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.FACILITY, facilityId, attrsToFetch);
}
@Override
public PerunAttribute getFacilityAttribute(Facility facility, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getFacilityAttribute(facility.getId(), attrToFetch);
}
@Override
public PerunAttribute getFacilityAttribute(Long facilityId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.FACILITY, facilityId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getGroupAttributes(Group group, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getGroupAttributes(group.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getGroupAttributes(Long groupId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.GROUP, groupId, attrsToFetch);
}
@Override
public PerunAttribute getGroupAttribute(Group group, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getGroupAttribute(group.getId(), attrToFetch);
}
@Override
public PerunAttribute getGroupAttribute(Long groupId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.GROUP, groupId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getUserAttributes(PerunUser user, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getUserAttributes(user.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getUserAttributes(Long userId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.USER, userId, attrsToFetch);
}
@Override
public PerunAttribute getUserAttribute(PerunUser user, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getUserAttribute(user.getId(), attrToFetch);
}
@Override
public PerunAttribute getUserAttribute(Long userId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.USER, userId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getVoAttributes(Vo vo, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getVoAttributes(vo.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getVoAttributes(Long voId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.VO, voId, attrsToFetch);
}
@Override
public PerunAttribute getVoAttribute(Vo vo, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getVoAttribute(vo.getId(), attrToFetch);
}
@Override
public PerunAttribute getVoAttribute(Long voId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.VO, voId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getResourceAttributes(Resource resource, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return this.getResourceAttributes(resource.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getResourceAttributes(Long resourceId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.RESOURCE, resourceId, attrsToFetch);
}
@Override
public PerunAttribute getResourceAttribute(Resource resource, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return this.getResourceAttribute(resource.getId(), attrToFetch);
}
@Override
public PerunAttribute getResourceAttribute(Long resourceId, String attrToFetch) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.RESOURCE, resourceId, attrToFetch);
}
@Override
public Set<String> getCapabilities(Facility facility, Set<String> groupNames,
String facilityCapabilitiesAttrName,
String resourceCapabilitiesAttrName)
{
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
if (facility == null) {
return new HashSet<>();
}
Set<String> capabilities = new HashSet<>();
Set<String> resourceGroupNames = new HashSet<>();
if (null != resourceCapabilitiesAttrName) {
List<Resource> resources = this.getAssignedRichResources(facility);
for (Resource resource : resources) {
PerunAttributeValue attrValue = this.getResourceAttributeValue(resource.getId(), resourceCapabilitiesAttrName);
List<String> resourceCapabilities = attrValue.valueAsList();
if (resourceCapabilities == null || resourceCapabilities.size() == 0) {
continue;
}
List<Group> groups = this.getAssignedGroups(resource.getId());
for (Group group : groups) {
resourceGroupNames.add(group.getName());
String groupName = group.getName();
if (resource.getVo() != null) {
groupName = resource.getVo().getShortName() + ':' + groupName;
}
group.setUniqueGroupName(groupName);
if (groupNames.contains(groupName)) {
log.trace("Group [{}] found in users groups, add capabilities [{}]", groupName, resourceCapabilities);
capabilities.addAll(resourceCapabilities);
} else {
log.trace("Group [{}] not found in users groups, continue to the next one", groupName);
}
}
}
}
if (null != facilityCapabilitiesAttrName && !Collections.disjoint(groupNames, resourceGroupNames)) {
Set<String> facilityCapabilities = this.getFacilityCapabilities(facility, facilityCapabilitiesAttrName);
capabilities.addAll(facilityCapabilities);
}
return capabilities;
}
@Override
public Set<String> getCapabilities(Facility facility, Map<Long, String> idToGnameMap,
String facilityCapabilitiesAttrName, String resourceCapabilitiesAttrName)
{
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
if (facility == null) {
return new HashSet<>();
}
return this.getCapabilities(facility, new HashSet<>(idToGnameMap.values()), facilityCapabilitiesAttrName,
resourceCapabilitiesAttrName);
}
@Override
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId, Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<Group> groups = this.getGroupsWhereUserIsActive(facilityId, userId);
Map<Long, String> voIdToShortNameMap = new HashMap<>();
groups.forEach(g -> {
if (!voIdToShortNameMap.containsKey(g.getVoId())) {
Vo vo = this.getVoById(g.getVoId());
if (vo != null) {
voIdToShortNameMap.put(vo.getId(), vo.getShortName());
}
}
g.setUniqueGroupName(voIdToShortNameMap.get(g.getVoId()) + ':' + g.getName());
});
return groups;
}
@Override
public Set<Long> getUserGroupsIds(Long userId, Long voId) {
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
Member member = getMemberByUser(userId, voId);
Set<Long> groups = new HashSet<>();
if (member != null) {
groups = getMemberGroups(member.getId()).stream().map(Group::getId).collect(Collectors.toSet());
}
return groups;
}
@Override
public boolean isValidMemberInGroupsAndVos(Long userId, Set<Long> mandatoryVos, Set<Long> mandatoryGroups,
Set<Long> envVos, Set<Long> envGroups)
{
if (!this.connectorRpc.isEnabled()) {
return false;
}
List<Member> members = getMembersByUser(userId);
Set<Long> foundVoIds = new HashSet<>();
Set<Long> foundGroupIds = new HashSet<>();
boolean skipGroups = mandatoryGroups.isEmpty() && envGroups.isEmpty();
for (Member m: members) {
if (MemberStatus.VALID.equals(m.getStatus())) {
foundVoIds.add(m.getVoId());
}
if (!skipGroups) {
foundGroupIds.addAll(getMemberGroups(m.getId()).stream().map(Model::getId).collect(Collectors.toList()));
}
}
return PerunAdapter.decideAccess(foundVoIds, foundGroupIds, mandatoryVos, mandatoryGroups, envVos, envGroups);
}
@Override
public boolean isValidMemberInGroupsAndVos(Long userId, Set<Long> vos, Set<Long> groups)
{
if (!this.connectorRpc.isEnabled()) {
return false;
}
List<Member> members = getMembersByUser(userId);
Set<Long> foundVoIds = new HashSet<>();
Set<Long> foundGroupIds = new HashSet<>();
boolean skipGroups = groups.isEmpty();
for (Member m: members) {
if (MemberStatus.VALID.equals(m.getStatus())) {
foundVoIds.add(m.getVoId());
}
if (!skipGroups) {
foundGroupIds.addAll(getMemberGroups(m.getId()).stream().map(Model::getId).collect(Collectors.toList()));
}
}
return PerunAdapter.decideAccess(foundVoIds, foundGroupIds, vos, groups);
}
@Override
public boolean isUserInVo(Long userId, String voShortName) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
if (userId == null) {
throw new IllegalArgumentException("No userId");
} else if (!StringUtils.hasText(voShortName)) {
throw new IllegalArgumentException("No voShortName");
}
Vo vo = getVoByShortName(voShortName);
if (vo == null || vo.getId() == null) {
log.debug("isUserInVo - No VO found, returning false");
return false;
}
try {
Member member = getMemberByUser(userId, vo.getId());
if (member == null) {
log.debug("isUserInVo - No member found, returning false");
return false;
}
return VALID.equals(member.getStatus());
} catch (Exception e) {
log.debug("isUserInVo - caught exception, probably user is not a member");
log.trace("{}", e.getMessage(), e);
return false;
}
}
@Override
public boolean hasApplicationForm(String voShortName) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
Vo vo = getVoByShortName(voShortName);
if (vo == null || vo.getId() == null) {
return false;
}
return hasApplicationForm(vo.getId());
}
@Override
public PerunUser getPerunUser(Long userId) {
if (!this.connectorRpc.isEnabled()) {
return null;
} else if (userId == null) {
throw new IllegalArgumentException("No userId");
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", userId);
JsonNode response = connectorRpc.post(USERS_MANAGER, "getUserById", map);
return RpcMapper.mapPerunUser(response);
}
@Override
public Set<Long> getUserVoIds(Long userId) {
if (!this.connectorRpc.isEnabled()) {
return Collections.emptySet();
} else if (userId == null) {
throw new IllegalArgumentException("No userId");
}
List<Member> members = getMembersByUser(userId);
Set<Long> voIds = new HashSet<>();
for (Member member: members) {
if (VALID == member.getStatus()) {
voIds.add(member.getVoId());
}
}
return voIds;
}
private Member getMemberByUser(Long userId, Long voId) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("user", userId);
params.put("vo", voId);
JsonNode jsonNode = connectorRpc.post(MEMBERS_MANAGER, "getMemberByUser", params);
return RpcMapper.mapMember(jsonNode);
}
private Set<Group> getGroupsWhereUserIsActive(Long facilityId, Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facilityId);
map.put("user", userId);
JsonNode res = connectorRpc.post(USERS_MANAGER, "getGroupsWhereUserIsActive", map);
return new HashSet<>(RpcMapper.mapGroups(res));
}
private Vo getVoById(Long voId) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", voId);
JsonNode res = connectorRpc.post(VOS_MANAGER, "getVoById", map);
return RpcMapper.mapVo(res);
}
private List<Group> getAssignedGroups(Long resourceId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("resource", resourceId);
JsonNode response = connectorRpc.post(RESOURCES_MANAGER, "getAssignedGroups", params);
return RpcMapper.mapGroups(response);
}
private Map<String, PerunAttributeValue> extractValues(Map<String, PerunAttribute> attributeMap) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttributeValue> resultMap = new LinkedHashMap<>();
for (Map.Entry<String, PerunAttribute> attrPair: attributeMap.entrySet()) {
String attrName = attrPair.getKey();
PerunAttribute attr = attrPair.getValue();
if (attr != null) {
resultMap.put(attrName, attr.toPerunAttributeValue());
}
}
return resultMap;
}
private Map<String, PerunAttribute> getAttributes(PerunEntityType entity, Long entityId, Collection<String> attrsToFetch) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
} else if (attrsToFetch == null || attrsToFetch.isEmpty()) {
return new HashMap<>();
}
Set<AttributeMapping> mappings;
switch (entity) {
case USER: mappings = this.getUserAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case FACILITY: mappings = this.getFacilityAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case VO: mappings = this.getVoAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case GROUP: mappings = this.getGroupAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case RESOURCE: mappings = this.getResourceAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
default: mappings = new HashSet<>();
break;
}
List<String> rpcNames = mappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList());
Map<String, Object> map = new LinkedHashMap<>();
map.put(entity.toString().toLowerCase(), entityId);
map.put("attrNames", rpcNames);
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, "getAttributes", map);
return RpcMapper.mapAttributes(res, mappings);
}
private List<Group> getMemberGroups(Long memberId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("member", memberId);
JsonNode response = connectorRpc.post(GROUPS_MANAGER, "getMemberGroups", map);
return RpcMapper.mapGroups(response);
}
private List<Member> getMembersByUser(Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("user", userId);
JsonNode jsonNode = connectorRpc.post(MEMBERS_MANAGER, "getMembersByUser", params);
return RpcMapper.mapMembers(jsonNode);
}
private List<Vo> getVosByShortNames(List<String> voShortNames) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Vo> vos = new ArrayList<>();
for (String shortName : voShortNames) {
Vo vo = getVoByShortName(shortName);
vos.add(vo);
}
return vos;
}
private Map<Long, MemberStatus> convertMembersListToStatusesMap(List<Member> userMembers) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<Long, MemberStatus> res = new HashMap<>();
for (Member m : userMembers) {
res.put(m.getVoId(), m.getStatus());
}
return res;
}
private Map<String, PerunAttributeValue> getUserExtSourceAttributeValues(Long uesId, Set<AttributeMapping> attrMappings) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("userExtSource", uesId);
map.put("attrNames", attrMappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList()));
JsonNode response = connectorRpc.post(ATTRIBUTES_MANAGER, "getAttributes", map);
Map<String, PerunAttribute> attributeMap = RpcMapper.mapAttributes(response, attrMappings);
return extractValues(attributeMap);
}
private List<Group> getAllowedGroups(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facility.getId());
JsonNode jsonNode = connectorRpc.post(FACILITIES_MANAGER, "getAllowedGroups", map);
List<Group> result = new ArrayList<>();
for (int i = 0; i < jsonNode.size(); i++) {
JsonNode groupNode = jsonNode.get(i);
result.add(RpcMapper.mapGroup(groupNode));
}
return result;
}
private boolean hasApplicationForm(Group group) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("group", group.getId());
try {
if (group.getName().equalsIgnoreCase("members")) {
log.debug("hasApplicationForm({}) continues to call regForm for VO {}", group, group.getVoId());
return hasApplicationForm(group.getVoId());
} else {
connectorRpc.post(REGISTRAR_MANAGER, "getApplicationForm", map);
}
} catch (Exception e) {
// when group does not have form exception is thrown. Every error thus is supposed as group without form
// this method will be used after calling other RPC methods - if RPC is not available other methods should discover it first
return false;
}
return true;
}
private boolean hasApplicationForm(Long voId) {
if (!this.connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("vo", voId);
try {
connectorRpc.post(REGISTRAR_MANAGER, "getApplicationForm", map);
} catch (Exception e) {
// when vo does not have form exception is thrown. Every error thus is supposed as vo without form
// this method will be used after calling other RPC methods - if RPC is not available other methods should discover it first
return false;
}
return true;
}
private List<Group> getGroupsWhereUserIsActive(Facility facility, Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facility.getId());
map.put("user", userId);
JsonNode jsonNode = connectorRpc.post(USERS_MANAGER, "getGroupsWhereUserIsActive", map);
return RpcMapper.mapGroups(jsonNode);
}
private Map<Long, Vo> convertVoListToMap(List<Vo> vos) {
if (!this.connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<Long, Vo> map = new HashMap<>();
for (Vo vo : vos) {
map.put(vo.getId(), vo);
}
return map;
}
private List<Resource> getAssignedResources(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facility.getId());
JsonNode res = connectorRpc.post(FACILITIES_MANAGER, "getAssignedResources", map);
return RpcMapper.mapResources(res);
}
private List<Resource> getAssignedRichResources(Facility facility) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("facility", facility.getId());
JsonNode res = connectorRpc.post(FACILITIES_MANAGER, "getAssignedRichResources", map);
return RpcMapper.mapResources(res);
}
private List<Group> getRichGroupsAssignedToResourceWithAttributesByNames(Resource resource, List<String> attrNames) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
Set<AttributeMapping> mappings = this.getGroupAttributesMappingService()
.getMappingsByIdentifiers(attrNames);
List<String> rpcNames = mappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList());
map.put("resource", resource.getId());
map.put("attrNames", rpcNames);
JsonNode res = connectorRpc.post(GROUPS_MANAGER, "getRichGroupsAssignedToResourceWithAttributesByNames", map);
List<Group> groups = new ArrayList<>();
for (int i = 0; i < res.size(); i++) {
JsonNode jsonNode = res.get(i);
Group group = RpcMapper.mapGroup(jsonNode);
JsonNode groupAttrs = jsonNode.get("attributes");
Map<String, JsonNode> attrsMap = new HashMap<>();
for (int j = 0; j < groupAttrs.size(); j++) {
JsonNode attr = groupAttrs.get(j);
String namespace = attr.get("namespace").textValue();
String friendlyName = attr.get("friendlyName").textValue();
attrsMap.put(namespace + ":" + friendlyName, attr);
}
group.setAttributes(attrsMap);
groups.add(group);
}
return groups;
}
private PerunAttribute getAttribute(PerunEntityType entity, Long entityId, String attributeName) {
if (!this.connectorRpc.isEnabled()) {
return null;
}
AttributeMapping mapping;
switch (entity) {
case USER: mapping = this.getUserAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case FACILITY: mapping = this.getFacilityAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case VO: mapping = this.getVoAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case GROUP: mapping = this.getGroupAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case RESOURCE: mapping = this.getResourceAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
default:
throw new IllegalArgumentException("Unrecognized entity");
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(entity.toString().toLowerCase(), entityId);
map.put("attributeName", mapping.getRpcName());
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, "getAttribute", map);
return RpcMapper.mapAttribute(res);
}
private List<UserExtSource> getUserExtSources(Long userId) {
if (!this.connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("user", userId);
JsonNode response = connectorRpc.post(USERS_MANAGER, "getUserExtSources", map);
return RpcMapper.mapUserExtSources(response);
}
private Set<String> getFacilityCapabilities(Facility facility, String capabilitiesAttrName) {
if (!this.connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<String> capabilities = new HashSet<>();
if (facility != null) {
PerunAttributeValue attr = getFacilityAttributeValue(facility, capabilitiesAttrName);
if (attr != null && attr.valueAsList() != null) {
capabilities = new HashSet<>(attr.valueAsList());
}
}
return capabilities;
}
public class PerunAdapterRpc
extends PerunAdapterWithMappingServices
implements PerunAdapterMethods, PerunAdapterMethodsRpc
{
// FIELD NAMES
protected static final String FIELD_ATTRIBUTES = "attributes";
protected static final String FIELD_NAMESPACE = "namespace";
protected static final String FIELD_FRIENDLY_NAME = "friendlyName";
protected static final String FIELD_VALUE = "value";
// OTHERS
protected static final String ORG_URL = "orgUrl";
protected static final String AFF_MAPPING = "affMapping";
// VARIABLES
private PerunConnectorRpc connectorRpc;
private String oidcClientIdAttr;
private String oidcCheckMembershipAttr;
private String orgUrlAttr;
private String affiliationsAttr;
public void setConnectorRpc(PerunConnectorRpc connectorRpc) {
this.connectorRpc = connectorRpc;
}
public void setOidcClientIdAttr(String oidcClientIdAttr) {
this.oidcClientIdAttr = oidcClientIdAttr;
}
public void setOidcCheckMembershipAttr(String oidcCheckMembershipAttr) {
this.oidcCheckMembershipAttr = oidcCheckMembershipAttr;
}
public void setOrgUrlAttr(String orgUrlAttr) {
this.orgUrlAttr = orgUrlAttr;
}
public void setAffiliationsAttr(String affiliationsAttr) {
this.affiliationsAttr = affiliationsAttr;
}
@Override
public PerunUser getPreauthenticatedUserId(String extLogin, String extSourceName) {
if (!connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_EXT_LOGIN, extLogin);
map.put(PARAM_EXT_SOURCE_NAME, extSourceName);
JsonNode response = connectorRpc.post(USERS_MANAGER, METHOD_GET_USER_BY_EXT_SOURCE_NAME_AND_EXT_LOGIN, map);
return RpcMapper.mapPerunUser(response);
}
@Override
public Facility getFacilityByClientId(String clientId) {
if (!connectorRpc.isEnabled()) {
return null;
} else if (!StringUtils.hasText(clientId)) {
return null;
}
AttributeMapping mapping = getFacilityAttributesMappingService().getMappingByIdentifier(oidcClientIdAttr);
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_ATTRIBUTE_NAME, mapping.getRpcName());
map.put(PARAM_ATTRIBUTE_VALUE, clientId);
JsonNode jsonNode = connectorRpc.post(FACILITIES_MANAGER, METHOD_GET_FACILITIES_BY_ATTRIBUTE, map);
return (jsonNode.size() > 0) ? RpcMapper.mapFacility(jsonNode.get(0)) : null;
}
@Override
public boolean isMembershipCheckEnabledOnFacility(Facility facility) {
if (!connectorRpc.isEnabled()) {
return false;
}
AttributeMapping mapping = getFacilityAttributesMappingService().getMappingByIdentifier(oidcCheckMembershipAttr);
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_FACILITY, facility.getId());
map.put(PARAM_ATTRIBUTE_NAME, mapping.getRpcName());
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ATTRIBUTE, map);
return res.get(FIELD_VALUE).asBoolean(false);
}
@Override
public boolean canUserAccessBasedOnMembership(Facility facility, Long userId, String ignoreAttr) {
if (!connectorRpc.isEnabled()) {
return true;
}
Set<Group> activeGroups = getGroupsWhereUserIsActive(facility.getId(), userId, ignoreAttr);
return !activeGroups.isEmpty();
}
@Override
public Map<Vo, List<Group>> getGroupsForRegistration(Facility facility, Long userId, List<String> voShortNames) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
List<Vo> vos = getVosByShortNames(voShortNames);
Map<Long, Vo> vosMap = convertVoListToMap(vos);
List<Member> userMembers = getMembersByUser(userId);
userMembers = new ArrayList<>(new HashSet<>(userMembers));
//Filter out vos where member is other than valid or expired. These vos cannot be used for registration
Map<Long, MemberStatus> memberVoStatuses = convertMembersListToStatusesMap(userMembers);
Map<Long, Vo> vosForRegistration = new HashMap<>();
for (Map.Entry<Long, Vo> entry : vosMap.entrySet()) {
if (memberVoStatuses.containsKey(entry.getKey())) {
MemberStatus status = memberVoStatuses.get(entry.getKey());
if (VALID.equals(status) || MemberStatus.EXPIRED.equals(status)) {
vosForRegistration.put(entry.getKey(), entry.getValue());
}
} else {
vosForRegistration.put(entry.getKey(), entry.getValue());
}
}
// filter groups only if their VO is in the allowed VOs and if they have registration form
List<Group> allowedGroups = getAllowedGroups(facility);
List<Group> groupsForRegistration = allowedGroups.stream()
.filter(group -> vosForRegistration.containsKey(group.getVoId()) && hasApplicationForm(group))
.collect(Collectors.toList());
// create map for processing
Map<Vo, List<Group>> result = new HashMap<>();
for (Group group : groupsForRegistration) {
Vo vo = vosMap.get(group.getVoId());
result.computeIfAbsent(vo, v -> new ArrayList<>());
result.get(vo).add(group);
}
return result;
}
@Override
public boolean groupWhereCanRegisterExists(Facility facility) {
if (!connectorRpc.isEnabled()) {
return false;
}
List<Group> allowedGroups = getAllowedGroups(facility);
if (!allowedGroups.isEmpty()) {
for (Group group : allowedGroups) {
if (hasApplicationForm(group)) {
return true;
}
}
}
return false;
}
@Override
public boolean isUserInGroup(Long userId, Long groupId) {
if (!connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> groupParams = new LinkedHashMap<>();
groupParams.put(PARAM_ID, groupId);
JsonNode groupResponse = connectorRpc.post(GROUPS_MANAGER, METHOD_GET_GROUP_BY_ID, groupParams);
Group group = RpcMapper.mapGroup(groupResponse);
Map<String, Object> memberParams = new LinkedHashMap<>();
memberParams.put(PARAM_VO, group.getVoId());
memberParams.put(PARAM_USER, userId);
JsonNode memberResponse = connectorRpc.post(MEMBERS_MANAGER, METHOD_GET_MEMBER_BY_USER, memberParams);
Member member = RpcMapper.mapMember(memberResponse);
Map<String, Object> isGroupMemberParams = new LinkedHashMap<>();
isGroupMemberParams.put(PARAM_GROUP, groupId);
isGroupMemberParams.put(PARAM_MEMBER, member.getId());
JsonNode res = connectorRpc.post(GROUPS_MANAGER, METHOD_IS_GROUP_MEMBER, isGroupMemberParams);
return res.asBoolean(false);
}
@Override
public boolean setUserAttribute(Long userId, PerunAttribute attribute) {
if (!connectorRpc.isEnabled()) {
return true;
}
JsonNode attributeJson = attribute.toJson();
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_USER, userId);
map.put(PARAM_ATTRIBUTE, attributeJson);
JsonNode response = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_SET_ATTRIBUTE, map);
return (response == null || response.isNull() || response instanceof NullNode);
}
@Override
public List<Affiliation> getUserExtSourcesAffiliations(Long userId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<UserExtSource> userExtSources = getUserExtSources(userId);
List<Affiliation> affiliations = new ArrayList<>();
AttributeMapping affMapping = new AttributeMapping(AFF_MAPPING, affiliationsAttr, "", ARRAY_TYPE);
AttributeMapping orgUrlMapping = new AttributeMapping(ORG_URL, orgUrlAttr, "", STRING_TYPE);
Set<AttributeMapping> attributeMappings = new HashSet<>(Arrays.asList(affMapping, orgUrlMapping));
for (UserExtSource ues : userExtSources) {
if (EXT_SOURCE_IDP.equals(ues.getExtSource().getType())) {
Map<String, PerunAttributeValue> uesAttrValues = getUserExtSourceAttributeValues(ues.getId(), attributeMappings);
long asserted = ues.getLastAccess().getTime() / 1000L;
String orgUrl = uesAttrValues.get(orgUrlMapping.getIdentifier()).valueAsString();
String affs = uesAttrValues.get(affMapping.getIdentifier()).valueAsString();
if (affs != null) {
for (String aff : affs.split(";")) {
String source = ((orgUrl != null) ? orgUrl : ues.getExtSource().getName());
Affiliation affiliation = new Affiliation(source, aff, asserted);
log.debug("found {} from IdP {} with orgURL {} asserted at {}", aff, ues.getExtSource().getName(),
orgUrl, asserted);
affiliations.add(affiliation);
}
}
}
}
return affiliations;
}
@Override
public List<Affiliation> getGroupAffiliations(Long userId, String groupAffiliationsAttr) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Affiliation> affiliations = new ArrayList<>();
List<Member> userMembers = getMembersByUser(userId);
for (Member member : userMembers) {
if (VALID.equals(member.getStatus())) {
List<Group> memberGroups = getMemberGroups(member.getId());
for (Group group : memberGroups) {
PerunAttributeValue attrValue = getGroupAttributeValue(group, groupAffiliationsAttr);
if (attrValue != null && attrValue.valueAsString() != null) {
long linuxTime = System.currentTimeMillis() / 1000L;
for (String value : attrValue.valueAsList()) {
Affiliation affiliation = new Affiliation(null, value, linuxTime);
log.debug("found {} on group {}", value, group.getName());
affiliations.add(affiliation);
}
}
}
}
}
return affiliations;
}
@Override
public List<String> getGroupsAssignedToResourcesWithUniqueNames(Facility facility) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Resource> resources = getAssignedResources(facility);
List<String> result = new ArrayList<>();
String voShortName = "urn:perun:group:attribute-def:virt:voShortName";
for (Resource res : resources) {
List<Group> groups = getRichGroupsAssignedToResourceWithAttributesByNames(res, Collections.singletonList(voShortName));
for (Group group : groups) {
if (group.getAttributeByUrnName(voShortName) != null &&
group.getAttributeByUrnName(voShortName).hasNonNull(FIELD_VALUE)) {
String value = group.getAttributeByUrnName(voShortName).get(FIELD_VALUE).textValue();
group.setUniqueGroupName(value + ":" + group.getName());
result.add(group.getUniqueGroupName());
}
}
}
return result;
}
@Override
public Map<String, PerunAttribute> getEntitylessAttributes(String attributeName) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, Object> attrNameMap = new LinkedHashMap<>();
attrNameMap.put(PARAM_ATTR_NAME, attributeName);
JsonNode entitylessAttributesJson = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ENTITYLESS_ATTRIBUTES, attrNameMap);
Long attributeDefinitionId = RpcMapper.mapAttribute(entitylessAttributesJson.get(0)).getId();
Map<String, Object> attributeDefinitionIdMap = new LinkedHashMap<>();
attributeDefinitionIdMap.put(PARAM_ATTRIBUTE_DEFINITION, attributeDefinitionId);
JsonNode entitylessKeysJson = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ENTITYLESS_KEYS, attributeDefinitionIdMap);
Map<String, PerunAttribute> result = new LinkedHashMap<>();
for (int i = 0; i < entitylessKeysJson.size(); i++) {
result.put(entitylessKeysJson.get(i).asText(), RpcMapper.mapAttribute(entitylessAttributesJson.get(i)));
}
return result;
}
@Override
public Vo getVoByShortName(String shortName) {
if (!connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put(PARAM_SHORT_NAME, shortName);
JsonNode jsonNode = connectorRpc.post(VOS_MANAGER, METHOD_GET_VO_BY_SHORT_NAME, params);
return RpcMapper.mapVo(jsonNode);
}
@Override
public Map<String, PerunAttributeValue> getUserAttributeValues(PerunUser user, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getUserAttributeValues(user.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getUserAttributeValues(Long userId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> userAttributes = getUserAttributes(userId, attrsToFetch);
return extractValues(userAttributes);
}
@Override
public PerunAttributeValue getUserAttributeValue(PerunUser user, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getUserAttributeValue(user.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getUserAttributeValue(Long userId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getUserAttribute(userId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getFacilityAttributeValues(Facility facility, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getFacilityAttributeValues(facility.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getFacilityAttributeValues(Long facilityId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> facilityAttributes = getFacilityAttributes(facilityId, attrsToFetch);
return extractValues(facilityAttributes);
}
@Override
public PerunAttributeValue getFacilityAttributeValue(Facility facility, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getFacilityAttributeValue(facility.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getFacilityAttributeValue(Long facilityId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getFacilityAttribute(facilityId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getVoAttributeValues(Vo vo, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getVoAttributeValues(vo.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getVoAttributeValues(Long voId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> voAttributes = getVoAttributes(voId, attrsToFetch);
return extractValues(voAttributes);
}
@Override
public PerunAttributeValue getVoAttributeValue(Vo vo, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getVoAttributeValue(vo.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getVoAttributeValue(Long voId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getVoAttribute(voId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getGroupAttributeValues(Group group, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getGroupAttributeValues(group.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getGroupAttributeValues(Long groupId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> groupAttributes = getGroupAttributes(groupId, attrsToFetch);
return extractValues(groupAttributes);
}
@Override
public PerunAttributeValue getGroupAttributeValue(Group group, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getGroupAttributeValue(group.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getGroupAttributeValue(Long groupId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getGroupAttribute(groupId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttributeValue> getResourceAttributeValues(Resource resource, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getResourceAttributeValues(resource.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttributeValue> getResourceAttributeValues(Long resourceId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttribute> resourceAttributes = getResourceAttributes(resourceId, attrsToFetch);
return extractValues(resourceAttributes);
}
@Override
public PerunAttributeValue getResourceAttributeValue(Resource resource, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getResourceAttributeValue(resource.getId(), attrToFetch);
}
@Override
public PerunAttributeValue getResourceAttributeValue(Long resourceId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getResourceAttribute(resourceId, attrToFetch).toPerunAttributeValue();
}
@Override
public Map<String, PerunAttribute> getFacilityAttributes(Facility facility, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getFacilityAttributes(facility.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getFacilityAttributes(Long facilityId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.FACILITY, facilityId, attrsToFetch);
}
@Override
public PerunAttribute getFacilityAttribute(Facility facility, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getFacilityAttribute(facility.getId(), attrToFetch);
}
@Override
public PerunAttribute getFacilityAttribute(Long facilityId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.FACILITY, facilityId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getGroupAttributes(Group group, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getGroupAttributes(group.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getGroupAttributes(Long groupId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.GROUP, groupId, attrsToFetch);
}
@Override
public PerunAttribute getGroupAttribute(Group group, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getGroupAttribute(group.getId(), attrToFetch);
}
@Override
public PerunAttribute getGroupAttribute(Long groupId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.GROUP, groupId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getUserAttributes(PerunUser user, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getUserAttributes(user.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getUserAttributes(Long userId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.USER, userId, attrsToFetch);
}
@Override
public PerunAttribute getUserAttribute(PerunUser user, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getUserAttribute(user.getId(), attrToFetch);
}
@Override
public PerunAttribute getUserAttribute(Long userId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.USER, userId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getVoAttributes(Vo vo, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getVoAttributes(vo.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getVoAttributes(Long voId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.VO, voId, attrsToFetch);
}
@Override
public PerunAttribute getVoAttribute(Vo vo, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getVoAttribute(vo.getId(), attrToFetch);
}
@Override
public PerunAttribute getVoAttribute(Long voId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.VO, voId, attrToFetch);
}
@Override
public Map<String, PerunAttribute> getResourceAttributes(Resource resource, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getResourceAttributes(resource.getId(), attrsToFetch);
}
@Override
public Map<String, PerunAttribute> getResourceAttributes(Long resourceId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
return getAttributes(PerunEntityType.RESOURCE, resourceId, attrsToFetch);
}
@Override
public PerunAttribute getResourceAttribute(Resource resource, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getResourceAttribute(resource.getId(), attrToFetch);
}
@Override
public PerunAttribute getResourceAttribute(Long resourceId, String attrToFetch) {
if (!connectorRpc.isEnabled()) {
return null;
}
return getAttribute(PerunEntityType.RESOURCE, resourceId, attrToFetch);
}
@Override
public Set<String> getCapabilities(Facility facility, Set<String> groupNames,
String facilityCapabilitiesAttrName,
String resourceCapabilitiesAttrName) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
if (facility == null) {
return new HashSet<>();
}
Set<String> capabilities = new HashSet<>();
Set<String> resourceGroupNames = new HashSet<>();
if (null != resourceCapabilitiesAttrName) {
List<Resource> resources = getAssignedRichResources(facility);
for (Resource resource : resources) {
PerunAttributeValue attrValue = getResourceAttributeValue(resource.getId(), resourceCapabilitiesAttrName);
List<String> resourceCapabilities = attrValue.valueAsList();
if (resourceCapabilities == null || resourceCapabilities.isEmpty()) {
continue;
}
List<Group> groups = getAssignedGroups(resource.getId());
for (Group group : groups) {
resourceGroupNames.add(group.getName());
String groupName = group.getName();
if (resource.getVo() != null) {
groupName = resource.getVo().getShortName() + ':' + groupName;
}
group.setUniqueGroupName(groupName);
if (groupNames.contains(groupName)) {
log.trace("Group [{}] found in users groups, add capabilities [{}]", groupName, resourceCapabilities);
capabilities.addAll(resourceCapabilities);
} else {
log.trace("Group [{}] not found in users groups, continue to the next one", groupName);
}
}
}
}
if (null != facilityCapabilitiesAttrName && !Collections.disjoint(groupNames, resourceGroupNames)) {
Set<String> facilityCapabilities = getFacilityCapabilities(facility, facilityCapabilitiesAttrName);
capabilities.addAll(facilityCapabilities);
}
return capabilities;
}
@Override
public Set<String> getCapabilities(Facility facility, Map<Long, String> idToGnameMap,
String facilityCapabilitiesAttrName, String resourceCapabilitiesAttrName) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
if (facility == null) {
return new HashSet<>();
}
return getCapabilities(facility, new HashSet<>(idToGnameMap.values()), facilityCapabilitiesAttrName,
resourceCapabilitiesAttrName);
}
@Override
public Set<Group> getGroupsWhereUserIsActiveWithUniqueNames(Long facilityId,
Long userId,
String resourceGroupEntitlementDisabledAttribute) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<Group> groups = getGroupsWhereUserIsActive(facilityId, userId, resourceGroupEntitlementDisabledAttribute);
Map<Long, String> voIdToShortNameMap = new HashMap<>();
groups.forEach(g -> {
if (!voIdToShortNameMap.containsKey(g.getVoId())) {
Vo vo = getVoById(g.getVoId());
if (vo != null) {
voIdToShortNameMap.put(vo.getId(), vo.getShortName());
}
}
g.setUniqueGroupName(voIdToShortNameMap.get(g.getVoId()) + ':' + g.getName());
});
return groups;
}
@Override
public Set<Long> getUserGroupsIds(Long userId, Long voId) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Member member = getMemberByUser(userId, voId);
Set<Long> groups = new HashSet<>();
if (member != null) {
groups = getMemberGroups(member.getId()).stream().map(Group::getId).collect(Collectors.toSet());
}
return groups;
}
@Override
public boolean isValidMemberInGroupsAndVos(Long userId, Set<Long> mandatoryVos, Set<Long> mandatoryGroups,
Set<Long> envVos, Set<Long> envGroups) {
if (!connectorRpc.isEnabled()) {
return false;
}
List<Member> members = getMembersByUser(userId);
Set<Long> foundVoIds = new HashSet<>();
Set<Long> foundGroupIds = new HashSet<>();
boolean skipGroups = mandatoryGroups.isEmpty() && envGroups.isEmpty();
for (Member m : members) {
if (MemberStatus.VALID.equals(m.getStatus())) {
foundVoIds.add(m.getVoId());
}
if (!skipGroups) {
foundGroupIds.addAll(getMemberGroups(m.getId()).stream().map(Model::getId).collect(Collectors.toList()));
}
}
return PerunAdapter.decideAccess(foundVoIds, foundGroupIds, mandatoryVos, mandatoryGroups, envVos, envGroups);
}
@Override
public boolean isValidMemberInGroupsAndVos(Long userId, Set<Long> vos, Set<Long> groups) {
if (!connectorRpc.isEnabled()) {
return false;
}
List<Member> members = getMembersByUser(userId);
Set<Long> foundVoIds = new HashSet<>();
Set<Long> foundGroupIds = new HashSet<>();
boolean skipGroups = groups.isEmpty();
for (Member m : members) {
if (MemberStatus.VALID.equals(m.getStatus())) {
foundVoIds.add(m.getVoId());
}
if (!skipGroups) {
foundGroupIds.addAll(getMemberGroups(m.getId()).stream().map(Model::getId).collect(Collectors.toList()));
}
}
return PerunAdapter.decideAccess(foundVoIds, foundGroupIds, vos, groups);
}
@Override
public boolean isUserInVo(Long userId, String voShortName) {
if (!connectorRpc.isEnabled()) {
return false;
}
if (userId == null) {
throw new IllegalArgumentException("No userId");
} else if (!StringUtils.hasText(voShortName)) {
throw new IllegalArgumentException("No voShortName");
}
Vo vo = getVoByShortName(voShortName);
if (vo == null || vo.getId() == null) {
log.debug("isUserInVo - No VO found, returning false");
return false;
}
try {
Member member = getMemberByUser(userId, vo.getId());
if (member == null) {
log.debug("isUserInVo - No member found, returning false");
return false;
}
return VALID.equals(member.getStatus());
} catch (Exception e) {
log.debug("isUserInVo - caught exception, probably user is not a member");
log.trace("{}", e.getMessage(), e);
return false;
}
}
@Override
public boolean hasApplicationForm(String voShortName) {
if (!connectorRpc.isEnabled()) {
return false;
}
Vo vo = getVoByShortName(voShortName);
if (vo == null || vo.getId() == null) {
return false;
}
return hasApplicationForm(vo.getId());
}
@Override
public PerunUser getPerunUser(Long userId) {
if (!connectorRpc.isEnabled()) {
return null;
} else if (userId == null) {
throw new IllegalArgumentException("No userId");
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_ID, userId);
JsonNode response = connectorRpc.post(USERS_MANAGER, METHOD_GET_USER_BY_ID, map);
return RpcMapper.mapPerunUser(response);
}
@Override
public Set<Long> getUserVoIds(Long userId) {
if (!connectorRpc.isEnabled()) {
return Collections.emptySet();
} else if (userId == null) {
throw new IllegalArgumentException("No userId");
}
List<Member> members = getMembersByUser(userId);
Set<Long> voIds = new HashSet<>();
for (Member member : members) {
if (VALID == member.getStatus()) {
voIds.add(member.getVoId());
}
}
return voIds;
}
private Member getMemberByUser(Long userId, Long voId) {
if (!connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put(PARAM_USER, userId);
params.put(PARAM_VO, voId);
JsonNode jsonNode = connectorRpc.post(MEMBERS_MANAGER, METHOD_GET_MEMBER_BY_USER, params);
return RpcMapper.mapMember(jsonNode);
}
private Set<Group> getGroupsWhereUserIsActive(Long facilityId, Long userId, String ignoreGroupsAttribute) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<Group> groups = new HashSet<>();
if (StringUtils.hasText(ignoreGroupsAttribute)) {
Set<Resource> resources = getResourcesAssignedToFacility(facilityId, userId, ignoreGroupsAttribute);
for (Resource resource : resources) {
groups.addAll(
getGroupsWhereUserIsActiveByResource(resource.getId(), userId)
);
}
} else {
groups.addAll(getGroupsWhereUserIsActiveByFacility(facilityId, userId));
}
return groups;
}
private Set<Group> getGroupsWhereUserIsActiveByFacility(Long facilityId, Long userId) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_FACILITY, facilityId);
map.put(PARAM_USER, userId);
JsonNode jsonNode = connectorRpc.post(USERS_MANAGER, METHOD_GET_GROUPS_WHERE_USER_IS_ACTIVE, map);
return new HashSet<>(RpcMapper.mapGroups(jsonNode));
}
private Set<Resource> getResourcesAssignedToFacility(Long facilityId, Long userId, String ignoreAttribute) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<Resource> resources = getAllowedResources(facilityId, userId);
Set<Resource> result = new HashSet<>();
for (Resource resource : resources) {
PerunAttributeValue attrValue = getResourceAttributeValue(resource.getId(), ignoreAttribute);
if (attrValue == null || attrValue.isNullValue() || !attrValue.valueAsBoolean()) {
result.add(resource);
} else {
log.debug(
"getResourcesAssignedToFacility - non null attr value for '{}', skipping resource '{}'",
ignoreAttribute, resource
);
}
}
return result;
}
private Set<Group> getGroupsWhereUserIsActiveByResource(Long resourceId, Long userId) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_USER, userId);
map.put(PARAM_RESOURCE, resourceId);
JsonNode res = connectorRpc.post(USERS_MANAGER, METHOD_GET_GROUPS_WHERE_USER_IS_ACTIVE, map);
return new HashSet<>(RpcMapper.mapGroups(res));
}
private Set<Resource> getAllowedResources(Long facilityId, Long userId) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_USER, userId);
map.put(PARAM_FACILITY, facilityId);
JsonNode res = connectorRpc.post(USERS_MANAGER, METHOD_GET_ALLOWED_RESOURCES, map);
return new HashSet<>(RpcMapper.mapResources(res));
}
private Vo getVoById(Long voId) {
if (!connectorRpc.isEnabled()) {
return null;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_ID, voId);
JsonNode res = connectorRpc.post(VOS_MANAGER, METHOD_GET_VO_BY_ID, map);
return RpcMapper.mapVo(res);
}
private List<Group> getAssignedGroups(Long resourceId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> params = new LinkedHashMap<>();
params.put(PARAM_RESOURCE, resourceId);
JsonNode response = connectorRpc.post(RESOURCES_MANAGER, METHOD_GET_ASSIGNED_GROUPS, params);
return RpcMapper.mapGroups(response);
}
private Map<String, PerunAttributeValue> extractValues(Map<String, PerunAttribute> attributeMap) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, PerunAttributeValue> resultMap = new LinkedHashMap<>();
for (Map.Entry<String, PerunAttribute> attrPair : attributeMap.entrySet()) {
String attrName = attrPair.getKey();
PerunAttribute attr = attrPair.getValue();
if (attr != null) {
resultMap.put(attrName, attr.toPerunAttributeValue());
}
}
return resultMap;
}
private Map<String, PerunAttribute> getAttributes(PerunEntityType entity, Long entityId, Collection<String> attrsToFetch) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
} else if (attrsToFetch == null || attrsToFetch.isEmpty()) {
return new HashMap<>();
}
Set<AttributeMapping> mappings;
switch (entity) {
case USER:
mappings = getUserAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case FACILITY:
mappings = getFacilityAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case VO:
mappings = getVoAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case GROUP:
mappings = getGroupAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
case RESOURCE:
mappings = getResourceAttributesMappingService()
.getMappingsByIdentifiers(attrsToFetch);
break;
default:
mappings = new HashSet<>();
break;
}
List<String> rpcNames = mappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList());
Map<String, Object> map = new LinkedHashMap<>();
map.put(entity.toString().toLowerCase(), entityId);
map.put(PARAM_ATTR_NAMES, rpcNames);
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ATTRIBUTES, map);
return RpcMapper.mapAttributes(res, mappings);
}
private List<Group> getMemberGroups(Long memberId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_MEMBER, memberId);
JsonNode response = connectorRpc.post(GROUPS_MANAGER, METHOD_GET_MEMBER_GROUPS, map);
return RpcMapper.mapGroups(response);
}
private List<Member> getMembersByUser(Long userId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> params = new LinkedHashMap<>();
params.put(PARAM_USER, userId);
JsonNode jsonNode = connectorRpc.post(MEMBERS_MANAGER, METHOD_GET_MEMBERS_BY_USER, params);
return RpcMapper.mapMembers(jsonNode);
}
private List<Vo> getVosByShortNames(List<String> voShortNames) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
List<Vo> vos = new ArrayList<>();
for (String shortName : voShortNames) {
Vo vo = getVoByShortName(shortName);
vos.add(vo);
}
return vos;
}
private Map<Long, MemberStatus> convertMembersListToStatusesMap(List<Member> userMembers) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<Long, MemberStatus> res = new HashMap<>();
for (Member m : userMembers) {
res.put(m.getVoId(), m.getStatus());
}
return res;
}
private Map<String, PerunAttributeValue> getUserExtSourceAttributeValues(Long uesId, Set<AttributeMapping> attrMappings) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_USER_EXT_SOURCE, uesId);
map.put(PARAM_ATTR_NAMES, attrMappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList()));
JsonNode response = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ATTRIBUTES, map);
Map<String, PerunAttribute> attributeMap = RpcMapper.mapAttributes(response, attrMappings);
return extractValues(attributeMap);
}
private List<Group> getAllowedGroups(Facility facility) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_FACILITY, facility.getId());
JsonNode jsonNode = connectorRpc.post(FACILITIES_MANAGER, METHOD_GET_ALLOWED_GROUPS, map);
List<Group> result = new ArrayList<>();
for (int i = 0; i < jsonNode.size(); i++) {
JsonNode groupNode = jsonNode.get(i);
result.add(RpcMapper.mapGroup(groupNode));
}
return result;
}
private boolean hasApplicationForm(Group group) {
if (!connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_GROUP, group.getId());
try {
if (group.getName().equalsIgnoreCase("members")) {
log.debug("hasApplicationForm({}) continues to call regForm for VO {}", group, group.getVoId());
return hasApplicationForm(group.getVoId());
} else {
connectorRpc.post(REGISTRAR_MANAGER, METHOD_GET_APPLICATION_FORM, map);
}
} catch (Exception e) {
// when group does not have form exception is thrown. Every error thus is supposed as group without form
// this method will be used after calling other RPC methods - if RPC is not available other methods should discover it first
return false;
}
return true;
}
private boolean hasApplicationForm(Long voId) {
if (!connectorRpc.isEnabled()) {
return false;
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_VO, voId);
try {
connectorRpc.post(REGISTRAR_MANAGER, METHOD_GET_APPLICATION_FORM, map);
} catch (Exception e) {
// when vo does not have form exception is thrown. Every error thus is supposed as vo without form
// this method will be used after calling other RPC methods - if RPC is not available other methods should discover it first
return false;
}
return true;
}
private Map<Long, Vo> convertVoListToMap(List<Vo> vos) {
if (!connectorRpc.isEnabled()) {
return new HashMap<>();
}
Map<Long, Vo> map = new HashMap<>();
for (Vo vo : vos) {
map.put(vo.getId(), vo);
}
return map;
}
private List<Resource> getAssignedResources(Facility facility) {
return getAssignedResources(facility.getId());
}
private List<Resource> getAssignedResources(Long facilityId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_FACILITY, facilityId);
JsonNode res = connectorRpc.post(FACILITIES_MANAGER, METHOD_GET_ASSIGNED_RESOURCES, map);
return RpcMapper.mapResources(res);
}
private List<Resource> getAssignedRichResources(Facility facility) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_FACILITY, facility.getId());
JsonNode res = connectorRpc.post(FACILITIES_MANAGER, METHOD_GET_ASSIGNED_RICH_RESOURCES, map);
return RpcMapper.mapResources(res);
}
private List<Group> getRichGroupsAssignedToResourceWithAttributesByNames(Resource resource, List<String> attrNames) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
Set<AttributeMapping> mappings = getGroupAttributesMappingService()
.getMappingsByIdentifiers(attrNames);
List<String> rpcNames = mappings.stream().map(AttributeMapping::getRpcName).collect(Collectors.toList());
map.put(PARAM_RESOURCE, resource.getId());
map.put(PARAM_ATTR_NAMES, rpcNames);
JsonNode res = connectorRpc.post(GROUPS_MANAGER, METHOD_GET_RICH_GROUPS_ASSIGNED_TO_RESOURCE_WITH_ATTRIBUTES_BY_NAMES, map);
List<Group> groups = new ArrayList<>();
for (int i = 0; i < res.size(); i++) {
JsonNode jsonNode = res.get(i);
Group group = RpcMapper.mapGroup(jsonNode);
JsonNode groupAttrs = jsonNode.get(FIELD_ATTRIBUTES);
Map<String, JsonNode> attrsMap = new HashMap<>();
for (int j = 0; j < groupAttrs.size(); j++) {
JsonNode attr = groupAttrs.get(j);
String namespace = attr.get(FIELD_NAMESPACE).textValue();
String friendlyName = attr.get(FIELD_FRIENDLY_NAME).textValue();
attrsMap.put(namespace + ":" + friendlyName, attr);
}
group.setAttributes(attrsMap);
groups.add(group);
}
return groups;
}
private PerunAttribute getAttribute(PerunEntityType entity, Long entityId, String attributeName) {
if (!connectorRpc.isEnabled()) {
return null;
}
AttributeMapping mapping;
switch (entity) {
case USER:
mapping = getUserAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case FACILITY:
mapping = getFacilityAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case VO:
mapping = getVoAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case GROUP:
mapping = getGroupAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
case RESOURCE:
mapping = getResourceAttributesMappingService()
.getMappingByIdentifier(attributeName);
break;
default:
throw new IllegalArgumentException("Unrecognized entity");
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(entity.toString().toLowerCase(), entityId);
map.put(PARAM_ATTRIBUTE_NAME, mapping.getRpcName());
JsonNode res = connectorRpc.post(ATTRIBUTES_MANAGER, METHOD_GET_ATTRIBUTE, map);
return RpcMapper.mapAttribute(res);
}
private List<UserExtSource> getUserExtSources(Long userId) {
if (!connectorRpc.isEnabled()) {
return new ArrayList<>();
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(PARAM_USER, userId);
JsonNode response = connectorRpc.post(USERS_MANAGER, METHOD_GET_USER_EXT_SOURCES, map);
return RpcMapper.mapUserExtSources(response);
}
private Set<String> getFacilityCapabilities(Facility facility, String capabilitiesAttrName) {
if (!connectorRpc.isEnabled()) {
return new HashSet<>();
}
Set<String> capabilities = new HashSet<>();
if (facility != null) {
PerunAttributeValue attr = getFacilityAttributeValue(facility, capabilitiesAttrName);
if (attr != null && attr.valueAsList() != null) {
capabilities = new HashSet<>(attr.valueAsList());
}
}
return capabilities;
}
}
......@@ -96,14 +96,18 @@ public class ClaimUtils {
return res;
}
public static Set<Group> getUserGroupsOnFacility(Facility facility, Long userId,
PerunAdapter perunAdapter, String claimName)
public static Set<Group> getUserGroupsOnFacility(Facility facility,
Long userId,
PerunAdapter perunAdapter,
String resourceGroupEntitlementDisabledAttribute,
String claimName)
{
Set<Group> userGroups = new HashSet<>();
if (facility == null) {
log.warn("{} - no facility provided when searching for user groups, will return empty set", claimName);
} else {
userGroups = perunAdapter.getGroupsWhereUserIsActiveWithUniqueNames(facility.getId(), userId);
userGroups = perunAdapter.getGroupsWhereUserIsActiveWithUniqueNames(
facility.getId(), userId, resourceGroupEntitlementDisabledAttribute);
}
log.trace("{} - found user groups: '{}'", claimName, userGroups);
return userGroups;
......
......@@ -45,7 +45,8 @@ public class EntitlementExtendedClaimSource extends EntitlementSource {
}
private Set<String> produceEntitlementsExtended(Facility facility, Long userId, PerunAdapter perunAdapter) {
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, userId, perunAdapter, getClaimName());
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(
facility, userId, perunAdapter, getClaimName(), getGroupEntitlementDisabledAttr());
Map<Long, String> groupIdToNameMap = super.getGroupIdToNameMap(userGroups, false);
Set<String> entitlements = new TreeSet<>();
this.fillUuidEntitlements(userGroups, entitlements);
......
......@@ -9,6 +9,7 @@ import cz.muni.ics.oidc.server.adapters.PerunAdapter;
import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext;
import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext;
import cz.muni.ics.oidc.server.claims.ClaimUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
......@@ -30,6 +31,10 @@ import java.util.TreeSet;
* </li>
* <li><b>custom.claim.[claimName].source.resourceCapabilities</b> - resource capabilities attribute name for resources</li>
* <li><b>custom.claim.[claimName].source.facilityCapabilities</b> - resource capabilities attribute name for facility</li>
* <li>
* <b>custom.claim.[claimName].source.groupEntitlementDisabledAttribute</b> - resource attribute which triggers
* if resource assigned groups should not be used for generating group entitlements.
* When not specified, all groups will be used</li>
* <li><b>custom.claim.[claimName].source.prefix</b> - string to be prepended to the value,</li>
* <li>
* <b>custom.claim.[claimName].source.authority</b> - string to be appended to the value, represents authority
......@@ -48,12 +53,17 @@ public class EntitlementSource extends GroupNamesSource {
protected static final String FORWARDED_ENTITLEMENTS = "forwardedEntitlements";
protected static final String RESOURCE_CAPABILITIES = "resourceCapabilities";
protected static final String FACILITY_CAPABILITIES = "facilityCapabilities";
protected static final String GROUP_ENTITLEMENT_DISABLED_ATTR = "groupEntitlementDisabledAttribute";
protected static final String PREFIX = "prefix";
protected static final String AUTHORITY = "authority";
private final String forwardedEntitlements;
private final String resourceCapabilities;
private final String facilityCapabilities;
@Getter
private final String groupEntitlementDisabledAttr;
private final String prefix;
private final String authority;
......@@ -63,6 +73,7 @@ public class EntitlementSource extends GroupNamesSource {
this.forwardedEntitlements = ClaimUtils.fillStringPropertyOrDefaultVal(FORWARDED_ENTITLEMENTS, ctx, null);
this.resourceCapabilities = ClaimUtils.fillStringPropertyOrDefaultVal(RESOURCE_CAPABILITIES, ctx, null);
this.facilityCapabilities = ClaimUtils.fillStringPropertyOrDefaultVal(FACILITY_CAPABILITIES, ctx, null);
this.groupEntitlementDisabledAttr = ClaimUtils.fillStringPropertyOrDefaultVal(GROUP_ENTITLEMENT_DISABLED_ATTR, ctx, null);
this.prefix = ClaimUtils.fillStringMandatoryProperty(PREFIX, ctx, getClaimName());
this.authority = ClaimUtils.fillStringMandatoryProperty(AUTHORITY, ctx, getClaimName());
......@@ -86,7 +97,7 @@ public class EntitlementSource extends GroupNamesSource {
PerunAdapter perunAdapter = pctx.getPerunAdapter();
Long userId = pctx.getPerunUserId();
Facility facility = pctx.getFacility();
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, userId, perunAdapter, getClaimName());
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, userId, perunAdapter, groupEntitlementDisabledAttr, getClaimName());
Set<String> entitlements = produceEntitlements(facility, userGroups, userId, perunAdapter);
JsonNode result = ClaimUtils.convertResultStringsToJsonArray(entitlements);
......
......@@ -49,7 +49,7 @@ public class GroupNamesSource extends ClaimSource {
log.trace("{} - produce group names with trimming 'members' part of the group names", getClaimName());
Facility facility = pctx.getFacility();
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, pctx.getPerunUserId(),
pctx.getPerunAdapter(), getClaimName());
pctx.getPerunAdapter(), getClaimName(), null);
return getGroupIdToNameMap(userGroups, true);
}
......
......@@ -58,7 +58,7 @@ public class ResourceAssignedActiveMemberGroupsClaimSource extends ClaimSource {
Long userId = pctx.getPerunUserId();
Facility facility = pctx.getFacility();
PerunAdapter perunAdapter = pctx.getPerunAdapter();
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, userId, perunAdapter, getClaimName());
Set<Group> userGroups = ClaimUtils.getUserGroupsOnFacility(facility, userId, perunAdapter, getClaimName(), null);
return getValuesFromAttribute(userGroups, perunAdapter);
}
......
......@@ -37,182 +37,232 @@ import java.util.Map;
@Slf4j
public class PerunConnectorRpc implements InitializingBean {
public static final String ATTRIBUTES_MANAGER = "attributesManager";
public static final String FACILITIES_MANAGER = "facilitiesManager";
public static final String GROUPS_MANAGER = "groupsManager";
public static final String MEMBERS_MANAGER = "membersManager";
public static final String REGISTRAR_MANAGER = "registrarManager";
public static final String SEARCHER = "searcher";
public static final String USERS_MANAGER = "usersManager";
public static final String VOS_MANAGER = "vosManager";
public static final String RESOURCES_MANAGER = "resourcesManager";
private String perunUrl;
private String perunUser;
private String perunPassword;
private boolean isEnabled;
private String serializer;
private int connectionRequestTimeout = 30000;
private int connectionTimeout = 30000;
private int responseTimeout = 60000;
private RestTemplate restTemplate;
public PerunConnectorRpc(String url,
String username,
String password,
String enabled,
String serializer,
int connectionRequestTimeout,
int connectionTimeout,
int responseTimeout)
{
this.isEnabled = Boolean.parseBoolean(enabled);
this.setPerunUrl(url);
this.setPerunUser(username);
this.setPerunPassword(password);
this.setSerializer(serializer);
this.setConnectionRequestTimeout(connectionRequestTimeout);
this.setConnectionTimeout(connectionTimeout);
this.setResponseTimeout(responseTimeout);
}
private void setEnabled(String enabled) {
this.isEnabled = Boolean.parseBoolean(enabled);
}
public boolean isEnabled() {
return isEnabled;
}
private void setPerunUrl(String perunUrl) {
if (!StringUtils.hasText(perunUrl)) {
throw new IllegalArgumentException("Perun URL cannot be null or empty");
} else if (perunUrl.endsWith("/")) {
perunUrl = perunUrl.substring(0, perunUrl.length() - 1);
}
this.perunUrl = perunUrl;
}
private void setPerunUser(String perunUser) {
if (!StringUtils.hasText(perunUser)) {
throw new IllegalArgumentException("Perun USER cannot be null or empty");
}
this.perunUser = perunUser;
}
private void setPerunPassword(String perunPassword) {
if (!StringUtils.hasText(perunPassword)) {
throw new IllegalArgumentException("Perun PASSWORD cannot be null or empty");
}
this.perunPassword = perunPassword;
}
private void setSerializer(String serializer) {
if (!StringUtils.hasText(serializer)) {
serializer = "json";
}
this.serializer = serializer;
}
private void setConnectionRequestTimeout(int connectionRequestTimeout) {
if (0 >= connectionRequestTimeout) {
throw new IllegalArgumentException("Connection request timeout must be greater than 0ms");
}
this.connectionRequestTimeout = connectionRequestTimeout;
}
private void setConnectionTimeout(int connectionTimeout) {
if (0 >= connectionTimeout) {
throw new IllegalArgumentException("Connection timeout must be greater than 0ms");
}
this.connectionTimeout = connectionTimeout;
}
private void setResponseTimeout(int responseTimeout) {
if (0 >= responseTimeout) {
throw new IllegalArgumentException("Response timeout must be greater than 0ms");
}
this.responseTimeout = responseTimeout;
}
@Override
public void afterPropertiesSet() {
restTemplate = new RestTemplate();
//HTTP connection pooling, see https://howtodoinjava.com/spring-restful/resttemplate-httpclient-java-config/
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(this.connectionRequestTimeout) // The timeout when requesting a connection from the connection manager
.setConnectTimeout(this.connectionTimeout) // Determines the timeout in milliseconds until a connection is established
.setSocketTimeout(this.responseTimeout) // The timeout for waiting for data
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
poolingConnectionManager.setMaxTotal(20); // maximum connections total
poolingConnectionManager.setDefaultMaxPerRoute(18);
ConnectionKeepAliveStrategy connectionKeepAliveStrategy = (response, context) -> {
HeaderElementIterator it = new BasicHeaderElementIterator
(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return 20000L;
};
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingConnectionManager)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
HttpComponentsClientHttpRequestFactory poolingRequestFactory = new HttpComponentsClientHttpRequestFactory();
poolingRequestFactory.setHttpClient(httpClient);
//basic authentication
List<ClientHttpRequestInterceptor> interceptors =
Collections.singletonList(new BasicAuthorizationInterceptor(perunUser, perunPassword));
InterceptingClientHttpRequestFactory authenticatingRequestFactory = new InterceptingClientHttpRequestFactory(poolingRequestFactory, interceptors);
restTemplate.setRequestFactory(authenticatingRequestFactory);
}
/**
* Make post call to Perun RPC
* @param manager String value representing manager to be called. Use constants from this class.
* @param method Method to be called (i.e. getUserById)
* @param map Map of parameters to be passed as request body
* @return Response from Perun
*/
@LogTimes
public JsonNode post(String manager, String method, Map<String, Object> map) {
if (!this.isEnabled) {
return JsonNodeFactory.instance.nullNode();
}
String actionUrl = perunUrl + '/' + serializer + '/' + manager + '/' + method;
//make the call
try {
log.debug("calling {} with {}", actionUrl, map);
return restTemplate.postForObject(actionUrl, map, JsonNode.class);
} catch (HttpClientErrorException ex) {
MediaType contentType = ex.getResponseHeaders().getContentType();
String body = ex.getResponseBodyAsString();
log.error("HTTP ERROR " + ex.getRawStatusCode() + " URL " + actionUrl + " Content-Type: " + contentType);
if ("json".equals(contentType.getSubtype())) {
try {
log.error(new ObjectMapper().readValue(body, JsonNode.class).path("message").asText());
} catch (IOException e) {
log.error("cannot parse error message from JSON", e);
}
} else {
log.error(ex.getMessage());
}
throw new RuntimeException("cannot connect to Perun RPC", ex);
}
}
public static final String ATTRIBUTES_MANAGER = "attributesManager";
public static final String FACILITIES_MANAGER = "facilitiesManager";
public static final String GROUPS_MANAGER = "groupsManager";
public static final String MEMBERS_MANAGER = "membersManager";
public static final String REGISTRAR_MANAGER = "registrarManager";
public static final String SEARCHER = "searcher";
public static final String USERS_MANAGER = "usersManager";
public static final String VOS_MANAGER = "vosManager";
public static final String RESOURCES_MANAGER = "resourcesManager";
public static final String PARAM_ID = "id";
public static final String PARAM_RESOURCE = "resource";
public static final String PARAM_FACILITY = "facility";
public static final String PARAM_GROUP = "group";
public static final String PARAM_VO = "vo";
public static final String PARAM_USER = "user";
public static final String PARAM_MEMBER = "member";
public static final String PARAM_USER_EXT_SOURCE = "userExtSource";
public static final String PARAM_ATTRIBUTE = "attribute";
public static final String PARAM_ATTRIBUTE_NAME = "attributeName";
public static final String PARAM_ATTRIBUTE_VALUE = "attributeValue";
public static final String PARAM_ATTRIBUTE_DEFINITION = "attributeDefinition";
public static final String PARAM_ATTR_NAMES = "attrNames";
public static final String PARAM_ATTR_NAME = "attrName";
public static final String PARAM_EXT_LOGIN = "extLogin";
public static final String PARAM_EXT_SOURCE_NAME = "extSourceName";
public static final String PARAM_SHORT_NAME = "shortName";
// METHODS
public static final String METHOD_GET_USER_BY_EXT_SOURCE_NAME_AND_EXT_LOGIN =
"getUserByExtSourceNameAndExtLogin";
public static final String METHOD_GET_FACILITIES_BY_ATTRIBUTE = "getFacilitiesByAttribute";
public static final String METHOD_GET_ATTRIBUTE = "getAttribute";
public static final String METHOD_GET_GROUP_BY_ID = "getGroupById";
public static final String METHOD_GET_MEMBER_BY_USER = "getMemberByUser";
public static final String METHOD_IS_GROUP_MEMBER = "isGroupMember";
public static final String METHOD_SET_ATTRIBUTE = "setAttribute";
public static final String EXT_SOURCE_IDP = "cz.metacentrum.perun.core.impl.ExtSourceIdp";
public static final String METHOD_GET_ENTITYLESS_ATTRIBUTES = "getEntitylessAttributes";
public static final String METHOD_GET_ENTITYLESS_KEYS = "getEntitylessKeys";
public static final String METHOD_GET_VO_BY_SHORT_NAME = "getVoByShortName";
public static final String METHOD_GET_USER_BY_ID = "getUserById";
public static final String METHOD_GET_GROUPS_WHERE_USER_IS_ACTIVE = "getGroupsWhereUserIsActive";
public static final String METHOD_GET_ALLOWED_RESOURCES = "getAllowedResources";
public static final String METHOD_GET_VO_BY_ID = "getVoById";
public static final String METHOD_GET_ASSIGNED_GROUPS = "getAssignedGroups";
public static final String METHOD_GET_ATTRIBUTES = "getAttributes";
public static final String METHOD_GET_MEMBER_GROUPS = "getMemberGroups";
public static final String METHOD_GET_MEMBERS_BY_USER = "getMembersByUser";
public static final String METHOD_GET_ALLOWED_GROUPS = "getAllowedGroups";
public static final String METHOD_GET_APPLICATION_FORM = "getApplicationForm";
public static final String METHOD_GET_ASSIGNED_RESOURCES = "getAssignedResources";
public static final String METHOD_GET_ASSIGNED_RICH_RESOURCES = "getAssignedRichResources";
public static final String METHOD_GET_RICH_GROUPS_ASSIGNED_TO_RESOURCE_WITH_ATTRIBUTES_BY_NAMES =
"getRichGroupsAssignedToResourceWithAttributesByNames";
public static final String METHOD_GET_USER_EXT_SOURCES = "getUserExtSources";
// VARIABLES
private String perunUrl;
private String perunUser;
private String perunPassword;
private boolean isEnabled;
private String serializer;
private int connectionRequestTimeout = 30000;
private int connectionTimeout = 30000;
private int responseTimeout = 60000;
private RestTemplate restTemplate;
public PerunConnectorRpc(String url,
String username,
String password,
String enabled,
String serializer,
int connectionRequestTimeout,
int connectionTimeout,
int responseTimeout) {
this.isEnabled = Boolean.parseBoolean(enabled);
this.setPerunUrl(url);
this.setPerunUser(username);
this.setPerunPassword(password);
this.setSerializer(serializer);
this.setConnectionRequestTimeout(connectionRequestTimeout);
this.setConnectionTimeout(connectionTimeout);
this.setResponseTimeout(responseTimeout);
}
private void setEnabled(String enabled) {
this.isEnabled = Boolean.parseBoolean(enabled);
}
public boolean isEnabled() {
return isEnabled;
}
private void setPerunUrl(String perunUrl) {
if (!StringUtils.hasText(perunUrl)) {
throw new IllegalArgumentException("Perun URL cannot be null or empty");
} else if (perunUrl.endsWith("/")) {
perunUrl = perunUrl.substring(0, perunUrl.length() - 1);
}
this.perunUrl = perunUrl;
}
private void setPerunUser(String perunUser) {
if (!StringUtils.hasText(perunUser)) {
throw new IllegalArgumentException("Perun USER cannot be null or empty");
}
this.perunUser = perunUser;
}
private void setPerunPassword(String perunPassword) {
if (!StringUtils.hasText(perunPassword)) {
throw new IllegalArgumentException("Perun PASSWORD cannot be null or empty");
}
this.perunPassword = perunPassword;
}
private void setSerializer(String serializer) {
if (!StringUtils.hasText(serializer)) {
serializer = "json";
}
this.serializer = serializer;
}
private void setConnectionRequestTimeout(int connectionRequestTimeout) {
if (0 >= connectionRequestTimeout) {
throw new IllegalArgumentException("Connection request timeout must be greater than 0ms");
}
this.connectionRequestTimeout = connectionRequestTimeout;
}
private void setConnectionTimeout(int connectionTimeout) {
if (0 >= connectionTimeout) {
throw new IllegalArgumentException("Connection timeout must be greater than 0ms");
}
this.connectionTimeout = connectionTimeout;
}
private void setResponseTimeout(int responseTimeout) {
if (0 >= responseTimeout) {
throw new IllegalArgumentException("Response timeout must be greater than 0ms");
}
this.responseTimeout = responseTimeout;
}
@Override
public void afterPropertiesSet() {
restTemplate = new RestTemplate();
//HTTP connection pooling, see https://howtodoinjava.com/spring-restful/resttemplate-httpclient-java-config/
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(this.connectionRequestTimeout) // The timeout when requesting a connection from the connection manager
.setConnectTimeout(this.connectionTimeout) // Determines the timeout in milliseconds until a connection is established
.setSocketTimeout(this.responseTimeout) // The timeout for waiting for data
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
poolingConnectionManager.setMaxTotal(20); // maximum connections total
poolingConnectionManager.setDefaultMaxPerRoute(18);
ConnectionKeepAliveStrategy connectionKeepAliveStrategy = (response, context) -> {
HeaderElementIterator it = new BasicHeaderElementIterator
(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return 20000L;
};
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingConnectionManager)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
HttpComponentsClientHttpRequestFactory poolingRequestFactory = new HttpComponentsClientHttpRequestFactory();
poolingRequestFactory.setHttpClient(httpClient);
//basic authentication
List<ClientHttpRequestInterceptor> interceptors =
Collections.singletonList(new BasicAuthorizationInterceptor(perunUser, perunPassword));
InterceptingClientHttpRequestFactory authenticatingRequestFactory = new InterceptingClientHttpRequestFactory(poolingRequestFactory, interceptors);
restTemplate.setRequestFactory(authenticatingRequestFactory);
}
/**
* Make post call to Perun RPC
*
* @param manager String value representing manager to be called. Use constants from this class.
* @param method Method to be called (i.e. getUserById)
* @param map Map of parameters to be passed as request body
* @return Response from Perun
*/
@LogTimes
public JsonNode post(String manager, String method, Map<String, Object> map) {
if (!this.isEnabled) {
return JsonNodeFactory.instance.nullNode();
}
String actionUrl = perunUrl + '/' + serializer + '/' + manager + '/' + method;
//make the call
try {
log.debug("calling {} with {}", actionUrl, map);
return restTemplate.postForObject(actionUrl, map, JsonNode.class);
} catch (HttpClientErrorException ex) {
MediaType contentType = ex.getResponseHeaders().getContentType();
String body = ex.getResponseBodyAsString();
log.error("HTTP ERROR " + ex.getRawStatusCode() + " URL " + actionUrl + " Content-Type: " + contentType);
if ("json".equals(contentType.getSubtype())) {
try {
log.error(new ObjectMapper().readValue(body, JsonNode.class).path("message").asText());
} catch (IOException e) {
log.error("cannot parse error message from JSON", e);
}
} else {
log.error(ex.getMessage());
}
throw new RuntimeException("cannot connect to Perun RPC", ex);
}
}
}
......@@ -23,7 +23,6 @@ public interface AuthProcFilterConstants {
String PARAM_ACCEPTED = "accepted";
String PARAM_ACR_VALUES = "acr_values";
String PARAM_MAX_AGE = "max_age";
String PARAM_POST_LOGOUT_REDIRECT_URI = "post_logout_redirect_uri";
String PARAM_STATE = "state";
String CLIENT_ID_PREFIX = "urn:cesnet:proxyidp:client_id:";
String AARC_IDP_HINT = "aarc_idp_hint";
......
......@@ -31,6 +31,11 @@ import static cz.muni.ics.openid.connect.request.ConnectRequestParameters.STATE;
*
* Configuration:
* - based on the configuration of bean "facilityAttrsConfig"
* Configuration of filter (replace [name] part with the name defined for the filter):
* <ul>
* <li><b>filter.[name].accessControlDisabledAttr</b> - resource attribute which triggers if resource assigned
* groups should not be used for controlling access. When not specified, all groups will be used.</li>
* </ul>
* @see FacilityAttrsConfig
* @see cz.muni.ics.oidc.server.filters.AuthProcFilter (basic configuration options)
*
......@@ -39,15 +44,19 @@ import static cz.muni.ics.openid.connect.request.ConnectRequestParameters.STATE;
@Slf4j
public class PerunAuthorizationFilter extends AuthProcFilter {
protected static final String ACCESS_CONTROL_DISABLED_ATTR = "accessControlDisabledAttr";
private final PerunAdapter perunAdapter;
private final FacilityAttrsConfig facilityAttrsConfig;
private final PerunOidcConfig config;
private final String accessControlDisabledAttr;
public PerunAuthorizationFilter(AuthProcFilterInitContext ctx) throws ConfigurationException {
super(ctx);
this.perunAdapter = ctx.getPerunAdapterBean();
this.config = ctx.getPerunOidcConfigBean();
this.facilityAttrsConfig = ctx.getBeanUtil().getBean(FacilityAttrsConfig.class);
this.accessControlDisabledAttr = ctx.getProperty(ACCESS_CONTROL_DISABLED_ATTR, null);
}
@Override
......@@ -65,7 +74,7 @@ public class PerunAuthorizationFilter extends AuthProcFilter {
}
return this.decideAccess(facility, user, req, res, params.getClientIdentifier(),
perunAdapter, facilityAttrsConfig);
perunAdapter, facilityAttrsConfig, accessControlDisabledAttr);
}
@Override
......@@ -73,9 +82,14 @@ public class PerunAuthorizationFilter extends AuthProcFilter {
return false;
}
private boolean decideAccess(Facility facility, PerunUser user, HttpServletRequest req,
HttpServletResponse response, String clientIdentifier, PerunAdapter perunAdapter,
FacilityAttrsConfig facilityAttrsConfig)
private boolean decideAccess(Facility facility,
PerunUser user,
HttpServletRequest req,
HttpServletResponse response,
String clientIdentifier,
PerunAdapter perunAdapter,
FacilityAttrsConfig facilityAttrsConfig,
String accessControlDisabledAttr)
{
Map<String, PerunAttributeValue> facilityAttributes = perunAdapter.getFacilityAttributeValues(
facility, facilityAttrsConfig.getMembershipAttrNames());
......@@ -85,7 +99,7 @@ public class PerunAuthorizationFilter extends AuthProcFilter {
return true;
}
if (perunAdapter.canUserAccessBasedOnMembership(facility, user.getId())) {
if (perunAdapter.canUserAccessBasedOnMembership(facility, user.getId(), accessControlDisabledAttr)) {
log.info("{} - user allowed to access the service", getFilterName());
return true;
} else {
......
......@@ -31,11 +31,12 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.saml.SAMLLogoutFilter;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
......@@ -45,8 +46,6 @@ import javax.servlet.http.HttpSession;
import java.text.ParseException;
import java.util.Map;
import static cz.muni.ics.oidc.server.filters.AuthProcFilterConstants.PARAM_POST_LOGOUT_REDIRECT_URI;
import static cz.muni.ics.oidc.server.filters.AuthProcFilterConstants.PARAM_STATE;
import static cz.muni.ics.oidc.server.filters.AuthProcFilterConstants.PARAM_TARGET;
/**
......@@ -69,9 +68,23 @@ public class EndSessionEndpoint {
public static final String URL = "endsession";
private static final String CLIENT_KEY = "client";
private static final String STATE_KEY = "state";
private static final String REDIRECT_URI_KEY = "redirectUri";
public static final String PARAM_POST_LOGOUT_REDIRECT_URI = "post_logout_redirect_uri";
public static final String PARAM_STATE = "state";
public static final String PARAM_CLIENT_ID = "client_id";
public static final String PARAM_ID_TOKEN_HINT = "id_token_hint";
private static final String SESSION_KEY_CLIENT = "client";
private static final String SESSION_KEY_STATE = "state";
private static final String SESSION_KEY_REDIRECT_URI = "redirect_uri";
private static final String MODEL_CLIENT_KEY = "client";
private static final String PREFIX_REDIRECT = "redirect:";
private final SelfAssertionValidator validator;
private final PerunOidcConfig perunOidcConfig;
......@@ -90,73 +103,67 @@ public class EndSessionEndpoint {
this.htmlClasses = htmlClasses;
}
@RequestMapping(value = "/" + URL, method = RequestMethod.GET)
public String endSession(@RequestParam(value = "id_token_hint", required = false) String idTokenHint,
@GetMapping(value = "/" + URL)
public String endSession(@RequestParam(value = PARAM_ID_TOKEN_HINT, required = false) String idTokenHint,
@RequestParam(value = PARAM_POST_LOGOUT_REDIRECT_URI, required = false) String postLogoutRedirectUri,
@RequestParam(value = STATE_KEY, required = false) String state,
@RequestParam(value = PARAM_STATE, required = false) String state,
@RequestParam(value = PARAM_CLIENT_ID, required = false) String clientId,
HttpServletRequest request,
HttpSession session,
Authentication auth, Map<String, Object> model)
Authentication auth,
Map<String, Object> model)
{
JWTClaimsSet idTokenClaims = null; // pulled from the parsed and validated ID token
ClientDetailsEntity client = null; // pulled from ID token's audience field
if (!Strings.isNullOrEmpty(postLogoutRedirectUri)) {
session.setAttribute(REDIRECT_URI_KEY, postLogoutRedirectUri);
session.setAttribute(SESSION_KEY_REDIRECT_URI, postLogoutRedirectUri);
}
if (!Strings.isNullOrEmpty(state)) {
session.setAttribute(STATE_KEY, state);
session.setAttribute(SESSION_KEY_STATE, state);
}
// parse the ID token hint to see if it's valid
if (!Strings.isNullOrEmpty(idTokenHint)) {
try {
JWT idToken = JWTParser.parse(idTokenHint);
if (validator.isValid(idToken)) {
// we issued this ID token, figure out who it's for
idTokenClaims = idToken.getJWTClaimsSet();
String clientId = Iterables.getOnlyElement(idTokenClaims.getAudience());
client = clientService.loadClientByClientId(clientId);
clientId = resolveClientIdFromTokenAndParameter(idTokenHint, clientId);
}
// save a reference in the session for us to pick up later
//session.setAttribute("endSession_idTokenHint_claims", idTokenClaims);
session.setAttribute(CLIENT_KEY, client);
}
} catch (ParseException e) {
// it's not a valid ID token, ignore it
log.debug("Invalid id token hint", e);
} catch (InvalidClientException e) {
if (StringUtils.hasText(clientId)) {
try {
client = clientService.loadClientByClientId(clientId);
session.setAttribute(SESSION_KEY_CLIENT, client);
} catch (InvalidClientException e) {
// couldn't find the client, ignore it
log.debug("Invalid client", e);
throw new InvalidRequestException(
"Client requesting the logout cannot be found. Is someone doing something nasty?"
);
}
}
// are we logged in or not?
if (auth == null || !request.isUserInRole("ROLE_USER")) {
// we're not logged in anyway, process the final redirect bits if needed
return processLogout(null, null, session);
// We're not logged in, anyway. Process the final redirect bits if needed.
return processLogout(null, null, request, session, model);
} else {
log.info("Logout confirmating for user {} from client {}", auth.getName(), client != null ? client.getClientName() : "unknown");
log.info("Display logout confirm prompt for user {} from client {}",
auth.getName(), client != null ? client.getClientName() : "unknown"
);
// we are logged in, need to prompt the user before we log out
model.put("client", client);
model.put("idToken", idTokenClaims);
model.put(MODEL_CLIENT_KEY, client);
ControllerUtils.setPageOptions(model, request, htmlClasses, perunOidcConfig);
return "logout";
}
}
@RequestMapping(value = "/" + URL, method = RequestMethod.POST)
@PostMapping(value = "/" + URL)
public String processLogout(@RequestParam(value = "approve", required = false) String approved,
@RequestParam(value = "deny", required = false) String deny,
HttpSession session)
HttpServletRequest request,
HttpSession session,
Map<String, Object> model)
{
String redirectUri = (String) session.getAttribute(REDIRECT_URI_KEY);
String state = (String) session.getAttribute(STATE_KEY);
ClientDetailsEntity client = (ClientDetailsEntity) session.getAttribute(CLIENT_KEY);
String redirectUri = (String) session.getAttribute(SESSION_KEY_REDIRECT_URI);
String state = (String) session.getAttribute(SESSION_KEY_STATE);
ClientDetailsEntity client = (ClientDetailsEntity) session.getAttribute(SESSION_KEY_CLIENT);
String redirectURL = null;
// if we have a client AND the client has post-logout redirect URIs
......@@ -164,10 +171,9 @@ public class EndSessionEndpoint {
if (isUriValid(redirectUri, client)) {
UriComponentsBuilder uri = UriComponentsBuilder.fromHttpUrl(redirectUri);
if (StringUtils.hasText(state)) {
uri = uri.queryParam("state", state);
uri = uri.queryParam(PARAM_STATE, state);
}
UriComponents uriComponents = uri.build();
log.trace("redirect URL: {}", uriComponents);
redirectURL = uriComponents.toString();
}
......@@ -175,22 +181,52 @@ public class EndSessionEndpoint {
String target = getRedirectUrl(redirectUri, state);
if (StringUtils.hasText(approved)) {
target = getLogoutUrl(target);
log.trace("redirecting to logout SAML and then {}", target);
return "redirect:" + target;
log.trace("Endsession - redirecting to SAML logout, then to {}", target);
return PREFIX_REDIRECT + target;
} else {
log.trace("redirecting to {}", target);
return "redirect:" + redirectURL;
log.trace("Endsession - redirecting to {}", target);
return PREFIX_REDIRECT + redirectURL;
}
} else {
if (StringUtils.hasText(approved)) {
log.trace("redirecting to logout SAML only");
return "redirect:" + getLogoutUrl(null);
log.trace("Endsession - redirecting to SAML logout only");
return PREFIX_REDIRECT + getLogoutUrl(null);
} else {
ControllerUtils.setPageOptions(model, request, htmlClasses, perunOidcConfig);
log.trace("Endsession - user denied the logout and we have no redirect, display logout denied page");
return "logout_denied";
}
}
}
private String resolveClientIdFromTokenAndParameter(String idTokenHint, String clientId) {
JWTClaimsSet idTokenClaims = null;
try {
JWT idToken = JWTParser.parse(idTokenHint);
if (validator.isValid(idToken)) {
idTokenClaims = idToken.getJWTClaimsSet();
}
} catch (ParseException e) {
// it's not a valid ID token, ignore it
log.debug("Invalid id token hint", e);
}
if (idTokenClaims != null) {
String clientIdFromToken = Iterables.getOnlyElement(idTokenClaims.getAudience());
if (StringUtils.hasText(clientId)) {
if (StringUtils.hasText(clientIdFromToken) && !clientIdFromToken.equals(clientId)) {
throw new InvalidRequestException(
"Client ID and client for which the ID token has been issued do not match. Is someone doing something nasty?"
);
}
} else {
clientId = clientIdFromToken;
}
}
return clientId;
}
private boolean isUriValid(String redirectUri, ClientDetailsEntity client) {
return StringUtils.hasText(redirectUri)
&& client != null
......
......@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>18.4.0</version>
<version>18.5.0</version>
<packaging>pom</packaging>
<modules>
......