Skip to content

Commit

Permalink
feat(core): virtual attribute for eligibilities
Browse files Browse the repository at this point in the history
* new user virtual attributed created for aggregating eligibilities attributes from obtained user ext sources (only the highest value is used for each key)
  • Loading branch information
Kuliak committed Jul 11, 2023
1 parent 42d3641 commit 76bd8c7
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7985,6 +7985,17 @@ protected void initialize() {
policies.add(Triple.of(Role.GROUPADMIN, READ, RoleObject.None));
attributes.put(attr, createInitialPolicyCollections(policies));

//urn:perun:user:attribute-def:virt:userEligibilities
attr = new AttributeDefinition();
attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT);
attr.setFriendlyName("userEligibilities");
attr.setDisplayName("user Eligibilities");
attr.setType(LinkedHashMap.class.getName());
attr.setDescription("Virtual attribute, which collects all eligibilities user ext source attributes " +
"with keys and values (map). Only the highest value is selected for each key.");
policies = new ArrayList<>();
attributes.put(attr, createInitialPolicyCollections(policies));

//urn:perun:group:attribute-def:virt:autoRegistrationEnabled
attr = new AttributeDefinition();
attr.setNamespace(AttributesManager.NS_GROUP_ATTR_VIRT);
Expand Down Expand Up @@ -8079,6 +8090,17 @@ protected void initialize() {
policies.add(Triple.of(Role.PROXY, WRITE, RoleObject.None));
attributes.put(attr, createInitialPolicyCollections(policies));

//urn_perun_ues_attribute_def_def_eligibilities
attr = new AttributeDefinition();
attr.setNamespace(AttributesManager.NS_UES_ATTR_DEF);
attr.setType(LinkedHashMap.class.getName());
attr.setFriendlyName("eligibilities");
attr.setDisplayName("eligibilities");
attr.setDescription("eligibilities");

policies = new ArrayList<>();
attributes.put(attr, createInitialPolicyCollections(policies));

//urn_perun_ues_attribute_def_def_IdPOrganizationName
attr = new AttributeDefinition();
attr.setNamespace(AttributesManager.NS_UES_ATTR_DEF);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package cz.metacentrum.perun.core.impl.modules.attributes;

import cz.metacentrum.perun.core.api.Attribute;
import cz.metacentrum.perun.core.api.AttributeDefinition;
import cz.metacentrum.perun.core.api.AttributesManager;
import cz.metacentrum.perun.core.api.User;
import cz.metacentrum.perun.core.api.UserExtSource;
import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException;
import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException;
import cz.metacentrum.perun.core.impl.PerunSessionImpl;
import cz.metacentrum.perun.core.implApi.modules.attributes.SkipValueCheckDuringDependencyCheck;
import cz.metacentrum.perun.core.implApi.modules.attributes.UserVirtualAttributeCollectedFromUserExtSource;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Virtual attribute for user's eligibilities.
* Attribute is calculated using all user's ext sources and their corresponding eligibilities
* attribute so that for each unique key (name) only the highest obtained value is selected.
*
* @author Luboslav Halama
*/
@SuppressWarnings("unused")
@SkipValueCheckDuringDependencyCheck
public class urn_perun_user_attribute_def_virt_userEligibilities extends UserVirtualAttributeCollectedFromUserExtSource {

private final static String USER_ELIGIBILITIES_FRIENDLY_NAME = "userEligibilities";
private final static String A_U_V_USER_ELIGIBILITIES = AttributesManager.NS_USER_ATTR_VIRT + ":" + USER_ELIGIBILITIES_FRIENDLY_NAME;

@Override
public String getSourceAttributeFriendlyName() {
return "eligibilities";
}

@Override
public String getDestinationAttributeFriendlyName() {
return USER_ELIGIBILITIES_FRIENDLY_NAME;
}

@Override
public Attribute getAttributeValue(PerunSessionImpl sess, User user, AttributeDefinition attributeDefinition) {
List<UserExtSource> userExtSources = sess.getPerunBl().getUsersManagerBl().getUserExtSources(sess, user);

Map<String, String> collectedEligibilities = new LinkedHashMap<>();

for (UserExtSource extSource : userExtSources) {
Attribute attribute;
try {
attribute = sess.getPerunBl().getAttributesManagerBl().getAttribute(sess, extSource,
AttributesManager.NS_UES_ATTR_DEF + ":" + getSourceAttributeFriendlyName());
} catch (AttributeNotExistsException | WrongAttributeAssignmentException e) {
throw new RuntimeException(e);
}

Map<String, String> eligibilities = attribute.valueAsMap();

// if no eligibilities were obtained, skip this ext source
if (eligibilities == null) {
continue;
}

for (String key : eligibilities.keySet()) {

// if given key is not present yet, store it
if (!collectedEligibilities.containsKey(key)) {
collectedEligibilities.put(key, eligibilities.get(key));
continue;
}

// if given is present, check if value should be updated
int timestamp = Integer.parseInt(eligibilities.get(key));
if (Integer.parseInt(collectedEligibilities.get(key)) < timestamp) {
collectedEligibilities.replace(key, eligibilities.get(key));
}
}
}

Attribute attribute = new Attribute(attributeDefinition);
attribute.setValue(collectedEligibilities);
return attribute;
}

// @Override
// public List<AttributeHandleIdentifier> getHandleIdentifiers() {
// List<AttributeHandleIdentifier> handleIdentifiers = super.getHandleIdentifiers();
// handleIdentifiers.add(auditEvent -> {
// if (auditEvent instanceof AttributeChangedForUser &&
// ((AttributeChangedForUser) auditEvent).getAttribute().getFriendlyName().equals(getDestinationAttributeFriendlyName())) {
// return ((AttributeChangedForUser) auditEvent).getUser().getId();
// } else {
// return null;
// }
// });
// return handleIdentifiers;
// }
//
// @Override
// public List<AuditEvent> resolveVirtualAttributeValueChange(PerunSessionImpl perunSession, AuditEvent message) throws AttributeNotExistsException, WrongAttributeAssignmentException, WrongReferenceAttributeValueException {
// List<AuditEvent> resolvingMessages = super.resolveVirtualAttributeValueChange(perunSession, message);
// if (message == null) return resolvingMessages;
//
// if (message instanceof AttributeSetForUser &&
// ((AttributeSetForUser) message).getAttribute().getFriendlyName().equals(getSecondarySourceAttributeFriendlyName())) {
// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES);
// resolvingMessages.add(new AttributeSetForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeSetForUser) message).getUser()));
// } else if (message instanceof AttributeChangedForUser) {
// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES);
// resolvingMessages.add(new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeChangedForUser) message).getUser()));
// } else if (message instanceof AttributeRemovedForUser) {
// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES);
// resolvingMessages.add(new AttributeRemovedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeRemovedForUser) message).getUser()));
// }
//
// return resolvingMessages;
// }

// private AuditEvent resolveEvent(PerunSessionImpl perunSession, User user) throws AttributeNotExistsException {
// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES);
// return new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), user);
// }

@Override
public AttributeDefinition getAttributeDefinition() {
AttributeDefinition attr = new AttributeDefinition();
attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT);
attr.setFriendlyName(USER_ELIGIBILITIES_FRIENDLY_NAME);
attr.setDisplayName("user eligibilities");
attr.setType(LinkedHashMap.class.getName());
attr.setDescription("Virtual attribute, which collects all eligibilities user ext source attributes " +
"with keys and values (map). Only the highest value is selected for each key.");
return attr;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package cz.metacentrum.perun.core.impl.modules.attributes;

import cz.metacentrum.perun.core.api.Attribute;
import cz.metacentrum.perun.core.api.AttributeDefinition;
import cz.metacentrum.perun.core.api.AttributesManager;
import cz.metacentrum.perun.core.api.ExtSource;
import cz.metacentrum.perun.core.api.ExtSourcesManager;
import cz.metacentrum.perun.core.api.User;
import cz.metacentrum.perun.core.api.UserExtSource;
import cz.metacentrum.perun.core.bl.AttributesManagerBl;
import cz.metacentrum.perun.core.bl.PerunBl;
import cz.metacentrum.perun.core.bl.UsersManagerBl;
import cz.metacentrum.perun.core.impl.PerunSessionImpl;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class urn_perun_user_attribute_def_virt_userEligibilitiesTest {

private static urn_perun_user_attribute_def_virt_userEligibilities classInstance;
private static PerunSessionImpl session;
private static User user;
private static Attribute uesAttribute1;
private static Attribute uesAttribute2;
private static Attribute userEligibilitiesAttribute;
private static AttributeDefinition userEligibilitiesAttributeDefinition;

@Before
public void setUp() throws Exception {
classInstance = new urn_perun_user_attribute_def_virt_userEligibilities();
session = mock(PerunSessionImpl.class);
user = new User();
UserExtSource extSource = new UserExtSource(new ExtSource("test ext source", ExtSourcesManager.EXTSOURCE_LDAP), "test-login");
UserExtSource extSource2 = new UserExtSource(new ExtSource("another test ext source", ExtSourcesManager.EXTSOURCE_LDAP), "test-login2");

userEligibilitiesAttributeDefinition = new AttributeDefinition();
userEligibilitiesAttributeDefinition.setNamespace(AttributesManager.NS_USER_ATTR_VIRT);
userEligibilitiesAttributeDefinition.setFriendlyName("userEligibilities");
userEligibilitiesAttributeDefinition.setDisplayName("user eligibilities");
userEligibilitiesAttributeDefinition.setType(HashMap.class.getName());
userEligibilitiesAttributeDefinition.setDescription("Virtual attribute, which collects all eligibilities user ext source attributes " +
"with keys and values (map). Only the highest value is selected for each key.");
userEligibilitiesAttribute = new Attribute(userEligibilitiesAttributeDefinition);

AttributeDefinition attrDef = new AttributeDefinition();
attrDef.setFriendlyName("eligibilities");
attrDef.setType(AttributesManager.NS_UES_ATTR_DEF);
attrDef.setDescription("eligibilities test attribute");
uesAttribute1 = new Attribute(attrDef);
uesAttribute2 = new Attribute(attrDef);

Map<String, String> attrValue = new LinkedHashMap<>();
attrValue.put("cesnet", "8150000");
uesAttribute1.setValue(attrValue);

attrValue = new LinkedHashMap<>();
attrValue.put("cesnet", "9999999");
attrValue.put("mu", "1000000");
uesAttribute2.setValue(attrValue);

when(session.getPerunBl()).thenReturn(mock(PerunBl.class));
when(session.getPerunBl().getUsersManagerBl()).thenReturn(mock(UsersManagerBl.class));
when(session.getPerunBl().getAttributesManagerBl()).thenReturn(mock(AttributesManagerBl.class));

when(session.getPerunBl().getUsersManagerBl().getUserExtSources(session, user)).thenReturn(List.of(extSource, extSource2));
when(session.getPerunBl().getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_VIRT + ":userEligibilities"))
.thenReturn(userEligibilitiesAttribute);
when(session.getPerunBl().getAttributesManagerBl().getAttribute(session, extSource, AttributesManager.NS_UES_ATTR_DEF + ":eligibilities"))
.thenReturn(uesAttribute1);
when(session.getPerunBl().getAttributesManagerBl().getAttribute(session, extSource2, AttributesManager.NS_UES_ATTR_DEF + ":eligibilities"))
.thenReturn(uesAttribute2);
}

@Test
public void getEligibilitiesAttributeValue() throws Exception {
System.out.println("getEligibilitiesAttributeValue()");

Attribute attr = classInstance.getAttributeValue(session, user, userEligibilitiesAttributeDefinition);
Map<String, String> values = (LinkedHashMap<String, String>) attr.getValue();

assertEquals(2, values.size());
assertTrue(values.keySet().containsAll(List.of("cesnet", "mu")));

for (String key : values.keySet()) {
String value = values.get(key);
if (key.equals("cesnet")) {
assertEquals("9999999", value);
} else {
assertEquals("1000000", value);
}
}
}
}
1 change: 1 addition & 0 deletions perun-db/addProxyRolePolicies
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ my @readAttributes = (
"urn:perun:user:attribute-def:virt:voPersonExternalAffiliation",
"urn:perun:user:attribute-def:virt:forwardedVoPersonExternalAffiliation",
"urn:perun:user:attribute-def:virt:sponsoredMembershipInOrganizations",
"urn:perun:user:attribute-def:virt:userEligibilities",
"urn:perun:user:attribute-def:def:preferredMail",
"urn:perun:user:attribute-def:def:timezone",
"urn:perun:user:attribute-def:def:preferredLanguage",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import cz.metacentrum.perun.core.api.Attribute;
import cz.metacentrum.perun.core.api.AttributeDefinition;
import cz.metacentrum.perun.core.api.PerunBean;
import cz.metacentrum.perun.core.api.exceptions.InternalErrorException;

public interface PerunAttribute<T extends PerunBean> {

Expand All @@ -25,6 +24,7 @@ public interface PerunAttributeNames {
public static final String perunAttrClientID = "OIDCClientID";
public static final String perunAttrGroupNames = "groupNames";
public static final String perunAttrInstitutionsCountries = "institutionsCountries";
public static final String perunAttrUserEligibilities = "userEligibilities";

//LDAP ATTRIBUTES NAMES
public static final String ldapAttrAssignedToResourceId = "assignedToResourceId";
Expand Down Expand Up @@ -74,6 +74,7 @@ public interface PerunAttributeNames {
public static final String ldapAttrAdminOfGroup = "adminOfGroup";
public static final String ldapAttrAdminOfFacility = "adminOfFacility";
public static final String ldapAttrUuid = "uuid";
public static final String ldapAttrUserEligibilities = "userEligibilities";

//LDAP OBJECT CLASSES
public static final String objectClassTop = "top";
Expand Down
15 changes: 15 additions & 0 deletions perun-ldapc/src/main/resources/perun-ldapc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,21 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/
</bean>
</property>
</bean>
<bean class="cz.metacentrum.perun.ldapc.model.impl.PerunAttributeDesc">
<property name="name" value="userEligibilities"/>
<property name="required" value="false"/>
<property name="multipleValuesExtractor">
<bean class="cz.metacentrum.perun.ldapc.model.impl.MultipleAttributeValueExtractor">
<property name="name" value="userEligibilities"/>
<property name="namespace" value="urn:perun:user:attribute-def:virt"/>
<property name="valueTransformer" >
<bean class="cz.metacentrum.perun.ldapc.beans.MapEntryValueTransformer">
<property name="separator" value="=" />
</bean>
</property>
</bean>
</property>
</bean>
</list>
</property>
<property name="attributeDescriptionsExt" ref="perunUserAttributesExt"/>
Expand Down
3 changes: 2 additions & 1 deletion perun-utils/ldapc-scripts/schemas/perun-schema.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ olcAttributeTypes: {83}( 1.3.6.1.4.1.8057.2.80.89 NAME 'securityText' DESC 'Secu
olcAttributeTypes: {84}( 1.3.6.1.4.1.8057.2.80.90 NAME 'earliestActiveLastAccess' DESC 'Timestamp of the earliest active IdP extSource lastAccess' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {85}( 1.3.6.1.4.1.25178.4.1.11 NAME 'voPersonExternalAffiliation' DESC 'voPerson Scoped External Affiliation' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
olcAttributeTypes: {86}( 1.3.6.1.4.1.8057.2.80.91 NAME 'forwardedVoPersonExternalAffiliation' DESC 'forwardedVoPerson Scoped External Affiliation' EQUALITY caseExactMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
olcAttributeTypes: {87}( 1.3.6.1.4.1.8057.2.80.92 NAME 'userEligibilities' DESC 'user ext source eligibilities attributes with only the highest value is selected for each key.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
-
replace: olcObjectClasses
olcObjectClasses: {0}( 1.3.6.1.4.1.8057.2.80.4 NAME 'perunUser' DESC 'User managed by Perun' SUP inetOrgPerson STRUCTURAL MUST ( perunUserId $ isServiceUser $ isSponsoredUser ) MAY ( preferredMail $ userCertificateSubject $ uidNumber $ login $ eduPersonPrincipalNames $ userPassword $ memberOfPerunVo $ libraryIDs $ schacHomeOrganizations $ eduPersonScopedAffiliations $ voPersonExternalAffiliation $ forwardedVoPersonExternalAffiliation $ bonaFideStatus $ groupNames $ institutionsCountries $ isCesnetEligible $ loa $ internalUserIdentifiers $ eduPersonOrcid $ loaFenix $ adminOfVo $ adminOfGroup $ adminOfFacility $ eduPersonEntitlement $ europeanStudentID $ eIDASPersonIdentifier $ timezone $ phone $ address $ aups $ tcsMails $ sshPublicKey $ sponsoredMembershipInOrganizations $ userIdentities $ schacPersonalUniqueCodes $ securityImage $ mfaEnforced $ mfaTokens $ uuid $ oouEinfraSignedV1 $ cscaleCompany $ cscaleUserCategory $ cscaleAcceptEmail $ cscaleResearchField $ cscaleUserFunction $ userAup $ cscaleCountry $ unscopedLogin $ mfaEnforceSettings $ securityText $ earliestActiveLastAccess) )
olcObjectClasses: {0}( 1.3.6.1.4.1.8057.2.80.4 NAME 'perunUser' DESC 'User managed by Perun' SUP inetOrgPerson STRUCTURAL MUST ( perunUserId $ isServiceUser $ isSponsoredUser ) MAY ( preferredMail $ userCertificateSubject $ uidNumber $ login $ eduPersonPrincipalNames $ userPassword $ memberOfPerunVo $ libraryIDs $ schacHomeOrganizations $ eduPersonScopedAffiliations $ voPersonExternalAffiliation $ forwardedVoPersonExternalAffiliation $ bonaFideStatus $ groupNames $ institutionsCountries $ isCesnetEligible $ loa $ internalUserIdentifiers $ eduPersonOrcid $ loaFenix $ adminOfVo $ adminOfGroup $ adminOfFacility $ eduPersonEntitlement $ europeanStudentID $ eIDASPersonIdentifier $ timezone $ phone $ address $ aups $ tcsMails $ sshPublicKey $ sponsoredMembershipInOrganizations $ userIdentities $ schacPersonalUniqueCodes $ securityImage $ mfaEnforced $ mfaTokens $ uuid $ oouEinfraSignedV1 $ cscaleCompany $ cscaleUserCategory $ cscaleAcceptEmail $ cscaleResearchField $ cscaleUserFunction $ userAup $ cscaleCountry $ unscopedLogin $ mfaEnforceSettings $ securityText $ earliestActiveLastAccess $ userEligibilities) )
olcObjectClasses: {1}( 1.3.6.1.4.1.8057.2.80.5 NAME 'perunGroup' DESC 'Group managed by Perun' SUP top STRUCTURAL MUST ( cn $ perunGroupId $ perunVoId $ perunUniqueGroupName ) MAY ( uniqueMember $ businessCategory $ seeAlso $ owner $ ou $ o $ description $ perunParentGroup $ perunParentGroupId $ assignedToResourceId $ adminOfVo $ adminOfGroup $ adminOfFacility $ groupAffiliations $ uuid ) )
olcObjectClasses: {2}( 1.3.6.1.4.1.8057.2.80.15 NAME 'perunResource' DESC 'Resource managed by Perun' SUP top STRUCTURAL MUST ( cn $ perunResourceId $ perunVoId $ perunFacilityId ) MAY (uniqueMember $ businessCategory $ seeAlso $ owner $ ou $ o $ description $ assignedGroupId $ perunFacilityDn $ capabilities $ isAssignedWithSubgroups $ uuid ) )
olcObjectClasses: {3}( 1.3.6.1.4.1.8057.2.80.6 NAME 'perunVO' DESC 'VO managed by Perun' SUP organization STRUCTURAL MUST perunVoId MAY ( uniqueMember $ aup ) )
Expand Down

0 comments on commit 76bd8c7

Please sign in to comment.