From 05fe69dd180761dc306192c56250cfbbf648be2d Mon Sep 17 00:00:00 2001 From: Michael Simon <simon@kit.edu> Date: Fri, 27 Sep 2024 09:12:48 +0200 Subject: [PATCH] ISSUE-196 add user update for attribute queries --- .../entity/attribute/value/ValueEntity.java | 12 +++ .../src/main/webapp/user/show-identity.xhtml | 2 + .../attribute/IncomingAttributesHandler.java | 4 + .../proc/SingleStringMergeValueProcessor.java | 3 + .../service/attribute/proc/ValueUpdater.java | 5 +- .../saml/idp/AttributeAuthorityService.java | 83 ++++++++++++++----- .../saml/idp/SamlAttributeTranscoder.java | 6 ++ 7 files changed, 93 insertions(+), 22 deletions(-) diff --git a/bwreg-entities/src/main/java/edu/kit/scc/webreg/entity/attribute/value/ValueEntity.java b/bwreg-entities/src/main/java/edu/kit/scc/webreg/entity/attribute/value/ValueEntity.java index a37d8e317..e976a36d7 100644 --- a/bwreg-entities/src/main/java/edu/kit/scc/webreg/entity/attribute/value/ValueEntity.java +++ b/bwreg-entities/src/main/java/edu/kit/scc/webreg/entity/attribute/value/ValueEntity.java @@ -1,5 +1,6 @@ package edu.kit.scc.webreg.entity.attribute.value; +import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -44,6 +45,9 @@ public class ValueEntity extends AbstractBaseEntity { @ManyToMany(mappedBy = "nextValues") private Set<ValueEntity> prevValues = new HashSet<>(); + @Column(name = "last_update") + protected Date lastUpdate; + @Transient private Boolean changed; @@ -102,4 +106,12 @@ public class ValueEntity extends AbstractBaseEntity { public void setChanged(Boolean changed) { this.changed = changed; } + + public Date getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(Date lastUpdate) { + this.lastUpdate = lastUpdate; + } } diff --git a/bwreg-webapp/src/main/webapp/user/show-identity.xhtml b/bwreg-webapp/src/main/webapp/user/show-identity.xhtml index a61c97925..1feee5db9 100644 --- a/bwreg-webapp/src/main/webapp/user/show-identity.xhtml +++ b/bwreg-webapp/src/main/webapp/user/show-identity.xhtml @@ -49,6 +49,7 @@ <li><h:outputText value="#{prev.id} (#{prev.attributeSet.user.id})" /></li> </ui:repeat> </ul> + <div style="font-size: 0.7rem;"><h:outputText value="#{value.lastUpdate}" /></div> </p:column> </p:dataTable> @@ -73,6 +74,7 @@ <li><h:outputText value="#{item}"/></li> </ui:repeat></ul> </p:outputPanel> + <div style="font-size: 0.7rem;"><h:outputText value="#{value.lastUpdate}" /></div> </p:column> </p:dataTable> </p:panel> diff --git a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/IncomingAttributesHandler.java b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/IncomingAttributesHandler.java index 7652a0930..35a67b547 100644 --- a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/IncomingAttributesHandler.java +++ b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/IncomingAttributesHandler.java @@ -3,6 +3,7 @@ package edu.kit.scc.webreg.service.attribute; import static edu.kit.scc.webreg.dao.ops.RqlExpressions.equal; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -133,6 +134,7 @@ public abstract class IncomingAttributesHandler<T extends IncomingAttributeEntit } ((StringValueEntity) value).setValueString(attributeValue); + value.setLastUpdate(new Date()); return ((StringValueEntity) value); } @@ -157,6 +159,7 @@ public abstract class IncomingAttributesHandler<T extends IncomingAttributeEntit } ((LongValueEntity) value).setValueLong(attributeValue); + value.setLastUpdate(new Date()); return ((LongValueEntity) value); } @@ -191,6 +194,7 @@ public abstract class IncomingAttributesHandler<T extends IncomingAttributeEntit } } + value.setLastUpdate(new Date()); return listValue; } diff --git a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/SingleStringMergeValueProcessor.java b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/SingleStringMergeValueProcessor.java index 36cb36379..b2d0f6065 100644 --- a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/SingleStringMergeValueProcessor.java +++ b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/SingleStringMergeValueProcessor.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,6 +58,7 @@ public class SingleStringMergeValueProcessor extends AbstractListProcessor { if (valueMap.isEmpty()) { NullValueEntity targetValue = (NullValueEntity) getValueUpdater().resolveValue(attribute, attributeSet, NullValueEntity.class); + targetValue.setLastUpdate(new Date()); targetValue.setEndValue(true); } else { @@ -65,6 +67,7 @@ public class SingleStringMergeValueProcessor extends AbstractListProcessor { StringValueEntity targetValue = (StringValueEntity) getValueUpdater().resolveValue(attribute, attributeSet, StringValueEntity.class); targetValue.setValueString(name); valueMap.get(name).stream().forEach(v -> v.getNextValues().add(targetValue)); + targetValue.setLastUpdate(new Date()); targetValue.setEndValue(true); } } diff --git a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/ValueUpdater.java b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/ValueUpdater.java index 402ee5dd5..a2f0977ca 100644 --- a/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/ValueUpdater.java +++ b/regapp-idty/src/main/java/edu/kit/scc/webreg/service/attribute/proc/ValueUpdater.java @@ -7,6 +7,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -65,6 +66,7 @@ public class ValueUpdater { targetValue.setValueList(new ArrayList<>()); targetValue.getValueList().clear(); targetValue.getValueList().addAll(values); + targetValue.setLastUpdate(new Date()); targetValue.setEndValue(true); } @@ -119,6 +121,7 @@ public class ValueUpdater { value.getNextValues().add(targetValue); } targetValue.setValueList(new ArrayList<>(values)); + targetValue.setLastUpdate(new Date()); targetValue.setEndValue(true); } @@ -169,7 +172,7 @@ public class ValueUpdater { copyStringListValue((StringListValueEntity) in, (StringListValueEntity) out); else if (in instanceof LongValueEntity) ((LongValueEntity) out).setValueLong(((LongValueEntity) in).getValueLong()); - + out.setLastUpdate(in.getLastUpdate()); } private void copyStringValue(StringValueEntity in, StringValueEntity out) { diff --git a/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/AttributeAuthorityService.java b/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/AttributeAuthorityService.java index 813da7f17..82ac68e2b 100644 --- a/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/AttributeAuthorityService.java +++ b/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/AttributeAuthorityService.java @@ -31,12 +31,20 @@ import edu.kit.scc.webreg.dao.SamlSpMetadataDao; import edu.kit.scc.webreg.dao.ScriptDao; import edu.kit.scc.webreg.entity.SamlAAConfigurationEntity; import edu.kit.scc.webreg.entity.SamlSpMetadataEntity; +import edu.kit.scc.webreg.entity.SamlUserEntity; import edu.kit.scc.webreg.entity.ScriptEntity; import edu.kit.scc.webreg.entity.UserEntity; import edu.kit.scc.webreg.entity.attribute.AttributeReleaseEntity; +import edu.kit.scc.webreg.entity.identity.IdentityEntity; +import edu.kit.scc.webreg.entity.oauth.OAuthUserEntity; +import edu.kit.scc.webreg.entity.oidc.OidcUserEntity; +import edu.kit.scc.webreg.exc.UserUpdateException; import edu.kit.scc.webreg.script.ScriptingEnv; import edu.kit.scc.webreg.service.attribute.release.AttributeBuilder; import edu.kit.scc.webreg.service.identity.IdentityAttributeResolver; +import edu.kit.scc.webreg.service.impl.OAuthUserUpdater; +import edu.kit.scc.webreg.service.impl.OidcUserUpdater; +import edu.kit.scc.webreg.service.impl.SamlUserUpdater; import edu.kit.scc.webreg.service.saml.Saml2ResponseValidationService; import edu.kit.scc.webreg.service.saml.SamlHelper; import edu.kit.scc.webreg.service.saml.SsoHelper; @@ -77,6 +85,15 @@ public class AttributeAuthorityService { @Inject private SamlAttributeTranscoder attributeTranscoder; + @Inject + private SamlUserUpdater samlUserUpdater; + + @Inject + private OidcUserUpdater oidcUserUpdater; + + @Inject + private OAuthUserUpdater oauthUserUpdater; + public Envelope processAttributeQuery(SamlAAConfigurationEntity aaConfig, AttributeQuery query) throws SamlAuthenticationException { @@ -133,12 +150,18 @@ public class AttributeAuthorityService { String nameIdValue = query.getSubject().getNameID().getValue(); String nameIdFormat = query.getSubject().getNameID().getFormat(); - UserEntity user = (UserEntity) invocable.invokeFunction("resolveUser", scriptingEnv, nameIdFormat, - nameIdValue, logger, spEntity, aaConfig); - if (user != null) { + final UserEntity user = resolveUser(invocable, nameIdFormat, nameIdValue, spEntity, aaConfig); + final IdentityEntity identity = resolveIdentity(user, invocable, nameIdFormat, nameIdValue, spEntity, + aaConfig); + if (identity != null) { + + // only update all user objects when configured, because this can take some time + if (spEntity.getGenericStore().containsKey("update_identity") + && spEntity.getGenericStore().get("update_identity").equalsIgnoreCase("true")) + identity.getUsers().stream().forEach(u -> updateUser(u)); AttributeReleaseEntity attributeRelease = attributeBuilder.requestAttributeRelease(spEntity, - user.getIdentity()); + identity); script = scriptDao.findByName(resolveAttributeScript); if (script == null) @@ -210,26 +233,44 @@ public class AttributeAuthorityService { return envelope; } - private AttributeStatement buildAttributeStatement(UserEntity user) { - AttributeStatement attributeStatement = samlHelper.create(AttributeStatement.class, - AttributeStatement.DEFAULT_ELEMENT_NAME); - attributeStatement.getAttributes().add(buildAttribute("urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "eduPersonPrincipalName", Attribute.URI_REFERENCE, user.getEppn())); - return attributeStatement; - } + private IdentityEntity resolveIdentity(UserEntity user, Invocable invocable, String nameIdFormat, + String nameIdValue, SamlSpMetadataEntity spEntity, SamlAAConfigurationEntity aaConfig) + throws ScriptException { + if (user != null) { + return user.getIdentity(); + } + + try { + return (IdentityEntity) invocable.invokeFunction("resolveIdentity", scriptingEnv, nameIdFormat, nameIdValue, + logger, spEntity, aaConfig); + } catch (NoSuchMethodException e) { + // Ignore exception. Better would be a check for method existance + return null; + } - private Attribute buildAttribute(String name, String friendlyName, String nameFormat, String... values) { - Attribute attribute = samlHelper.create(Attribute.class, Attribute.DEFAULT_ELEMENT_NAME); - attribute.setName(name); - attribute.setFriendlyName(friendlyName); - attribute.setNameFormat(nameFormat); + } - for (String value : values) { - XSString xsany = samlHelper.create(XSString.class, XSString.TYPE_NAME, AttributeValue.DEFAULT_ELEMENT_NAME); - xsany.setValue(value); - attribute.getAttributeValues().add(xsany); + private UserEntity resolveUser(Invocable invocable, String nameIdFormat, String nameIdValue, + SamlSpMetadataEntity spEntity, SamlAAConfigurationEntity aaConfig) throws ScriptException { + try { + return (UserEntity) invocable.invokeFunction("resolveUser", scriptingEnv, nameIdFormat, nameIdValue, logger, + spEntity, aaConfig); + } catch (NoSuchMethodException e) { + // Ignore exception. Better would be a check for method existance + return null; } - return attribute; + } + + private void updateUser(UserEntity user) { + try { + if (user instanceof SamlUserEntity) + samlUserUpdater.updateUserFromHomeOrg((SamlUserEntity) user, null, "attribue-query", null); + else if (user instanceof OidcUserEntity) + oidcUserUpdater.updateUserFromHomeOrg((OidcUserEntity) user, null, "attribue-query", null); + else if (user instanceof OAuthUserEntity) + oauthUserUpdater.updateUserFromHomeOrg((OAuthUserEntity) user, null, "attribue-query", null); + } catch (UserUpdateException e) { + } } } diff --git a/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/SamlAttributeTranscoder.java b/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/SamlAttributeTranscoder.java index 5a0267b7a..8f3f254ab 100644 --- a/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/SamlAttributeTranscoder.java +++ b/regapp-saml-idp/src/main/java/edu/kit/scc/webreg/saml/idp/SamlAttributeTranscoder.java @@ -43,10 +43,16 @@ public class SamlAttributeTranscoder { new SingleStringValueTranscoder(samlHelper, "urn:oid:2.5.4.4", "sn", Attribute.BASIC)); transcoderMap.put("given_name", new SingleStringValueTranscoder(samlHelper, "urn:oid:2.5.4.42", "givenName", Attribute.BASIC)); + transcoderMap.put("email", + new SingleStringValueTranscoder(samlHelper, "urn:oid:0.9.2342.19200300.100.1.3", "email", Attribute.BASIC)); transcoderMap.put("eduperson_principal_name", new SingleStringValueTranscoder(samlHelper, "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", "eduPersonPrincipalName", Attribute.URI_REFERENCE)); transcoderMap.put("eduperson_entitlement", new SingleStringValueTranscoder(samlHelper, "urn:oid:1.3.6.1.4.1.5923.1.1.1.7", "eduPersonEntitlement", Attribute.BASIC)); + transcoderMap.put("eduperson_assurance", new SingleStringValueTranscoder(samlHelper, + "urn:oid:1.3.6.1.4.1.5923.1.1.1.11", "edupersonAssurance", Attribute.BASIC)); + transcoderMap.put("voperson_external_affiliation", new SingleStringValueTranscoder(samlHelper, + "urn:oid:1.3.6.1.4.1.25178.4.1.11", "voPersonExternalAffiliation", Attribute.BASIC)); } public Assertion convertAttributes(AttributeReleaseEntity attributeRelease, SamlAAConfigurationEntity aaConfig, -- GitLab