diff --git a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/adapters/impl/PerunAdapterRpc.java b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/adapters/impl/PerunAdapterRpc.java
index 05501b4c4395eb565b6b4d45ecb4920573d4d0e7..326e06ccf8f7abec9a1d4b095dee245fd71d80f5 100644
--- a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/adapters/impl/PerunAdapterRpc.java
+++ b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/adapters/impl/PerunAdapterRpc.java
@@ -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,1354 +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, String ignoreAttr) {
-		if (!this.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 (!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,
-																String resourceGroupEntitlementDisabledAttribute)
-	{
-		if (!this.connectorRpc.isEnabled()) {
-			return new HashSet<>();
-		}
-
-		Set<Group> groups = this.getGroupsWhereUserIsActive(facilityId, userId, resourceGroupEntitlementDisabledAttribute);
-
-		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, String ignoreGroupsAttribute) {
-		if (!this.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 (!this.connectorRpc.isEnabled()) {
-			return new HashSet<>();
-		}
-
-		Map<String, Object> map = new LinkedHashMap<>();
-		map.put("facility", facilityId);
-		map.put("user", userId);
-		JsonNode jsonNode = connectorRpc.post(USERS_MANAGER, "getGroupsWhereUserIsActive", map);
-
-		return new HashSet<>(RpcMapper.mapGroups(jsonNode));
-	}
-
-	private Set<Resource> getResourcesAssignedToFacility(Long facilityId, Long userId, String ignoreAttribute) {
-		if (!this.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 (!this.connectorRpc.isEnabled()) {
-			return new HashSet<>();
-		}
-
-		Map<String, Object> map = new LinkedHashMap<>();
-		map.put("user", userId);
-		map.put("resource", resourceId);
-
-		JsonNode res = connectorRpc.post(USERS_MANAGER, "getGroupsWhereUserIsActive", map);
-		return new HashSet<>(RpcMapper.mapGroups(res));
-	}
-
-	private Set<Resource> getAllowedResources(Long facilityId, Long userId) {
-		if (!this.connectorRpc.isEnabled()) {
-			return new HashSet<>();
-		}
-
-		Map<String, Object> map = new LinkedHashMap<>();
-		map.put("user", userId);
-		map.put("facility", facilityId);
-
-		JsonNode res = connectorRpc.post(USERS_MANAGER, "getAllowedResources", map);
-		return new HashSet<>(RpcMapper.mapResources(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 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) {
-		return getAssignedResources(facility.getId());
-	}
-
-	private List<Resource> getAssignedResources(Long facilityId) {
-		if (!this.connectorRpc.isEnabled()) {
-			return new ArrayList<>();
-		}
-
-		Map<String, Object> map = new LinkedHashMap<>();
-		map.put("facility", facilityId);
-
-		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;
+    }
 
 }
diff --git a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/connectors/PerunConnectorRpc.java b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/connectors/PerunConnectorRpc.java
index 4a5e1ce6bb12a2dec2b9679232743d0db7e666eb..e590c9acb2421143067dc23699bfc1d0baf44619 100644
--- a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/connectors/PerunConnectorRpc.java
+++ b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/connectors/PerunConnectorRpc.java
@@ -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);
+        }
+    }
 
 }