diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java
new file mode 100644
index 0000000000..715eb8513e
--- /dev/null
+++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.datamodel.legalentity;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Class to store IDs.
+ */
+public class MCRIdentifier {
+
+ private final String type;
+
+ private final String value;
+
+ /**
+ * Constructs new MCRIdentifier object with type and value.
+ *
+ * @param type the id type
+ * @param value the id value
+ */
+ public MCRIdentifier(String type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ /**
+ * Returns the id type.
+ *
+ * @return id type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the id value.
+ *
+ * @return id value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final MCRIdentifier identifier = (MCRIdentifier) obj;
+ return Objects.equals(type, identifier.type) && Objects.equals(value, identifier.value);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.ROOT, "%s:%s", type, value);
+ }
+}
diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java
new file mode 100644
index 0000000000..b35a01bf73
--- /dev/null
+++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.datamodel.legalentity;
+
+import java.util.Set;
+
+import org.mycore.common.config.MCRConfiguration2;
+
+/**
+ * Services that implement this interface should search for all identifiers of a specific legal entity (e.g. a person)
+ * using a specific, identifying {@link MCRIdentifier}, or add an identifier to the legal entity. The identifier
+ * can be any key-value pair that can uniquely identify a legal entity. The interface is intentionally generic to allow
+ * different identifier schemes and lookup implementations.
+ */
+public interface MCRLegalEntityService {
+
+ /**
+ * Finds all identifiers of a legal entity determined by a specific identifier.
+ * @param identifier unique identifier of legal entity, not null
+ * @return a set of identifiers a legal entity owns
+ */
+ Set findAllIdentifiers(MCRIdentifier identifier);
+
+ /**
+ * Adds an identifier to a legal entity. The entity is determined by a specific, given identifier
+ * @param primaryIdentifier unique identifier of legal entity, not null
+ * @param identifierToAdd the identifier to add, not null
+ */
+ void addIdentifier(MCRIdentifier primaryIdentifier, MCRIdentifier identifierToAdd);
+
+ /**
+ * Get configured singleton service implementation.
+ */
+ static MCRLegalEntityService obtainInstance() {
+ return InstanceHolder.SHARED_INSTANCE;
+ }
+
+ class InstanceHolder {
+ private static final MCRLegalEntityService SHARED_INSTANCE = MCRConfiguration2.getInstanceOfOrThrow(
+ MCRLegalEntityService.class, "MCR.LegalEntityService.Class");
+ }
+
+}
diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java
new file mode 100644
index 0000000000..58614313fa
--- /dev/null
+++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.datamodel.legalentity;
+
+import java.util.Set;
+
+/**
+ * This implementation is a fallback only. Another implementation of {@link MCRLegalEntityService} should
+ * be configured when using the service.
+ */
+public class MCRNoOpLegalEntityService implements MCRLegalEntityService {
+
+ @Override
+ public Set findAllIdentifiers(MCRIdentifier identifier) {
+ return Set.of();
+ }
+
+ @Override
+ public void addIdentifier(MCRIdentifier primaryIdentifier, MCRIdentifier identifierToAdd) {
+ // no-op
+ }
+}
diff --git a/mycore-base/src/main/resources/config/mycore.properties b/mycore-base/src/main/resources/config/mycore.properties
index fd97718947..f2545a93f1 100644
--- a/mycore-base/src/main/resources/config/mycore.properties
+++ b/mycore-base/src/main/resources/config/mycore.properties
@@ -759,3 +759,9 @@ MCR.Category.LinkService=org.mycore.datamodel.classifications2.impl.MCRCategLink
# Rate-Limit-Resolver
##############################################################################
MCR.URIResolver.ModuleResolver.ratelimit=org.mycore.common.xml.MCRRateLimitResolver
+
+##############################################################################
+# LegalEntityService
+##############################################################################
+# No-op Implementation of MCRLegalEntityService for querying UserIdentifier
+MCR.LegalEntityService.Class=org.mycore.datamodel.legalentity.MCRNoOpLegalEntityService
diff --git a/mycore-mods/pom.xml b/mycore-mods/pom.xml
index 6991bc5f15..998a88e427 100644
--- a/mycore-mods/pom.xml
+++ b/mycore-mods/pom.xml
@@ -128,6 +128,10 @@
org.mycore
mycore-solr
+
+ org.mycore
+ mycore-user2
+
xalan
xalan
diff --git a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java
new file mode 100644
index 0000000000..1694432127
--- /dev/null
+++ b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java
@@ -0,0 +1,160 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.mods;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jdom2.Element;
+import org.mycore.access.MCRAccessException;
+import org.mycore.common.MCRConstants;
+import org.mycore.common.MCRPersistenceException;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRLegalEntityService;
+import org.mycore.datamodel.metadata.MCRMetadataManager;
+import org.mycore.datamodel.metadata.MCRObject;
+import org.mycore.datamodel.metadata.MCRObjectID;
+import org.mycore.user2.MCRUser;
+import org.mycore.user2.MCRUserAttribute;
+import org.mycore.user2.MCRUserManager;
+
+/**
+ * This class identifies {@link MCRUser users} by their user ID and looks up their identifiers by loading the
+ * correlating modsperson metadata through the modsperson-{@link MCRUserAttribute attribute}
+ * attached to the user entity.
+ * If this attribute is not present, an empty set is returned.
+ * New attributes are added to the modsperson metadata and are not added to the user entity.
+ */
+public class MCRMODSPersonIdentifierService implements MCRLegalEntityService {
+
+ private static final Logger LOGGER = LogManager.getLogger();
+
+ public static final String MODSPERSON_ATTR_NAME = "id_modsperson";
+
+ public static final String USERID = "userid";
+
+ public static final String MODS_NAME = "name";
+
+ public static final String MODS_NAMEIDENTIFIER = "nameIdentifier";
+
+ public static final String TYPE = "type";
+
+
+ /**
+ * Gets all {@link MCRIdentifier MCRIdentifiers} of a modsperson by reference to a {@link org.mycore.user2.MCRUser}
+ * and its modsperson id.
+ * @param userId the user id connected to the modsperson
+ * @return all known identifiers or an empty set
+ */
+ @Override
+ public Set findAllIdentifiers(MCRIdentifier userId) {
+ return getIdentifiers(userId, null);
+ }
+
+ /**
+ * Adds a {@link MCRIdentifier MCRIdentifiers} to a modsperson by reference to a {@link org.mycore.user2.MCRUser}
+ * and its modsperson id.
+ * @param userId the user id connected to the modsperson
+ * @param attributeToAdd the nameIdentifier to add to the modsperson
+ */
+ @Override
+ public void addIdentifier(MCRIdentifier userId, MCRIdentifier attributeToAdd) {
+ Optional modspersonOptional = findModspersonByUsername(userId);
+ if (modspersonOptional.isEmpty()) {
+ return;
+ }
+ MCRMODSWrapper wrapper = new MCRMODSWrapper(modspersonOptional.get());
+ Element modsName = wrapper.getMODS().getChild(MODS_NAME, MCRConstants.MODS_NAMESPACE);
+ if (modsName == null) {
+ return;
+ }
+ Element nameIdentifier = new Element(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE)
+ .setAttribute(TYPE, attributeToAdd.getType())
+ .setText(attributeToAdd.getValue());
+ modsName.addContent(nameIdentifier);
+ try {
+ MCRMetadataManager.update(modspersonOptional.get());
+ } catch (MCRAccessException | MCRPersistenceException e) {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Could not update modsperson object for user id {}",
+ userId.getValue(), e);
+ }
+ }
+ }
+
+ /**
+ * Helper method to search for identifiers in a modsperson by a user-ID.
+ * @param userId the user id connected to the modsperson
+ * @param identifierType optional type filter, leave null for no filter
+ * @return a set of all identifiers found
+ */
+ private Set getIdentifiers(MCRIdentifier userId, String identifierType) {
+ Optional modspersonOptional = findModspersonByUsername(userId);
+ if (modspersonOptional.isEmpty()) {
+ return Collections.emptySet();
+ }
+ MCRMODSWrapper wrapper = new MCRMODSWrapper(modspersonOptional.get());
+ Element modsName = wrapper.getMODS().getChild(MODS_NAME, MCRConstants.MODS_NAMESPACE);
+ if (modsName == null) {
+ return Collections.emptySet();
+ }
+ if (identifierType != null) {
+ return modsName.getChildren(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE)
+ .stream().filter(e -> identifierType.equals(e.getAttributeValue(TYPE)))
+ .map(e -> new MCRIdentifier(e.getAttributeValue(TYPE), e.getText()))
+ .collect(Collectors.toSet());
+ }
+ return modsName.getChildren(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE)
+ .stream().map(e -> new MCRIdentifier(e.getAttributeValue(TYPE), e.getText()))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Takes a username and returns an Optional with the referenced modsperson.
+ * @param userId the user id
+ * @return a nullable Optional that might contain a modsperson
+ */
+ private Optional findModspersonByUsername(MCRIdentifier userId) {
+ if (userId == null || !USERID.equals(userId.getType())) {
+ return Optional.empty();
+ }
+ MCRUser user = MCRUserManager.getUser(userId.getValue());
+ if (user == null) {
+ return Optional.empty();
+ }
+ String modspersonId = user.getUserAttribute(MODSPERSON_ATTR_NAME);
+ if (modspersonId == null) {
+ return Optional.empty();
+ }
+ try {
+ MCRObject modsperson = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(modspersonId));
+ return Optional.of(modsperson);
+ } catch (MCRPersistenceException e) {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Could not retrieve modsperson object for user id {} (modspersonId={})",
+ userId.getValue(), modspersonId, e);
+ }
+ return Optional.empty();
+ }
+ }
+}
diff --git a/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java b/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java
new file mode 100644
index 0000000000..62f4876081
--- /dev/null
+++ b/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java
@@ -0,0 +1,151 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.mods;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Set;
+
+import org.jdom2.Document;
+import org.jdom2.JDOMException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mycore.access.MCRAccessBaseImpl;
+import org.mycore.access.MCRAccessException;
+import org.mycore.common.MCRTestConfiguration;
+import org.mycore.common.MCRTestProperty;
+import org.mycore.common.content.MCRURLContent;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.datamodel.metadata.MCRMetadataManager;
+import org.mycore.datamodel.metadata.MCRObject;
+import org.mycore.datamodel.metadata.MCRObjectMetadataTest;
+import org.mycore.test.MCRJPAExtension;
+import org.mycore.test.MCRMetadataExtension;
+import org.mycore.test.MyCoReTest;
+import org.mycore.user2.MCRUser;
+import org.mycore.user2.MCRUserManager;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@MyCoReTest
+@ExtendWith({ MCRJPAExtension.class, MCRMetadataExtension.class })
+@MCRTestConfiguration(properties = {
+ @MCRTestProperty(key = "MCR.Metadata.Type.modsperson", string = "true"),
+ @MCRTestProperty(key = "MCR.MODS.Types", string = "mods,modsperson"),
+ @MCRTestProperty(key = "MCR.MODS.NewObjectType", string = "mods"),
+ @MCRTestProperty(key = "MCR.Access.Class", classNameOf = MCRAccessBaseImpl.class)
+})
+public class MCRMODSPersonIdentifierServiceTest {
+
+ private static final String ORCID_1 = "0000-0001-2345-6789";
+
+ private static final String ORCID_2 = "0000-0002-3456-7895";
+
+ private static final String ORCID_3 = "0000-0003-4567-8985";
+
+ private static final String SCOPUS = "87654321";
+
+ MCRMODSPersonIdentifierService service;
+
+ MCRUser user;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ URL url1 = MCRObjectMetadataTest.class.getResource(
+ "/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml");
+ Document doc1 = new MCRURLContent(url1).asXML();
+ MCRObject obj1 = new MCRObject(doc1);
+ MCRMetadataManager.create(obj1);
+
+ user = new MCRUser("john");
+ user.setRealName("John Doe");
+ user.setUserAttribute("id_modsperson", "junit_modsperson_00000001");
+ MCRUserManager.createUser(user);
+
+ service = new MCRMODSPersonIdentifierService();
+ }
+
+ @Test
+ public final void testFindAllIdentifiers() {
+ Set allIdentifiers = service.findAllIdentifiers(
+ new MCRIdentifier("userid", user.getUserID()));
+ Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1),
+ new MCRIdentifier("orcid", ORCID_2),
+ new MCRIdentifier("orcid", ORCID_3),
+ new MCRIdentifier("scopus", SCOPUS),
+ new MCRIdentifier("something", "abcd"));
+ assertEquals(expected, allIdentifiers);
+ }
+
+ @Test
+ public final void testAddIdentifier() throws MCRAccessException, IOException, JDOMException {
+ URL url2 = MCRObjectMetadataTest.class.getResource(
+ "/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml");
+ Document doc2 = new MCRURLContent(url2).asXML();
+ MCRObject obj2 = new MCRObject(doc2);
+ MCRMetadataManager.create(obj2);
+
+ MCRUser user2 = new MCRUser("jane");
+ user2.setRealName("Jane Doe");
+ user2.setUserAttribute("id_modsperson", "junit_modsperson_00000002");
+ MCRUserManager.createUser(user2);
+
+ final MCRIdentifier userid = new MCRIdentifier("userid", user2.getUserID());
+ Set allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(2, allIdentifiers.size());
+
+ service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_1)); // don't add id twice
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(2, allIdentifiers.size());
+
+ service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_3));
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(3, allIdentifiers.size());
+ Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1),
+ new MCRIdentifier("orcid", ORCID_2),
+ new MCRIdentifier("orcid", ORCID_3));
+ assertEquals(expected, allIdentifiers);
+ }
+
+ @Test
+ public final void testAddIdentifierNoModsperson() {
+ Set allIdentifiers = service.findAllIdentifiers(new MCRIdentifier("userid", "noname"));
+ assertEquals(0, allIdentifiers.size());
+
+ MCRUser user3 = new MCRUser("james");
+ user3.setRealName("James Doe");
+ user3.setUserAttribute("id_orcid", ORCID_1);
+ MCRUserManager.createUser(user3);
+
+ final MCRIdentifier userid = new MCRIdentifier("userid", user3.getUserID());
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(0, allIdentifiers.size());
+
+ service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_2));
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(0, allIdentifiers.size());
+
+ user3.setUserAttribute("id_modsperson", "junit_modsperson_00000404");
+ MCRUserManager.updateUser(user3);
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(0, allIdentifiers.size());
+ }
+
+}
diff --git a/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml
new file mode 100644
index 0000000000..b0dae76303
--- /dev/null
+++ b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ Doe
+ John
+ 0000-0001-2345-6789
+ 0000-0002-3456-7895
+ 0000-0003-4567-8985
+ 87654321
+ abcd
+
+
+
+
+
+
diff --git a/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml
new file mode 100644
index 0000000000..4e6ed2415b
--- /dev/null
+++ b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ Doe
+ Jane
+ 0000-0001-2345-6789
+ 0000-0002-3456-7895
+
+
+
+
+
+
diff --git a/mycore-orcid2/pom.xml b/mycore-orcid2/pom.xml
index 208ab56d43..15e88b3fad 100644
--- a/mycore-orcid2/pom.xml
+++ b/mycore-orcid2/pom.xml
@@ -125,6 +125,12 @@
junit-jupiter-api
test
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
org.mycore
mycore-classifications
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java
index 6112c5788e..45f4138ecf 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java
@@ -41,7 +41,7 @@
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.mods.MCRMODSWrapper;
import org.mycore.orcid2.exception.MCRORCIDException;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
/**
* Provides utility methods.
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java
index 5bfaadb435..25d1d137ac 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java
@@ -28,10 +28,11 @@
import java.util.stream.Collectors;
import org.mycore.common.config.MCRConfiguration2;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRLegalEntityService;
import org.mycore.orcid2.MCRORCIDConstants;
import org.mycore.orcid2.client.MCRORCIDCredential;
import org.mycore.orcid2.exception.MCRORCIDException;
-import org.mycore.orcid2.util.MCRIdentifier;
import org.mycore.orcid2.util.MCRORCIDJSONMapper;
import org.mycore.orcid2.validation.MCRORCIDValidationHelper;
import org.mycore.user2.MCRUser;
@@ -72,6 +73,8 @@ public class MCRORCIDUser {
private final MCRUser user;
+ private MCRLegalEntityService legalEntityService = MCRLegalEntityService.obtainInstance();
+
/**
* Wraps MCRUser to MCRORCIDUser.
*
@@ -81,6 +84,17 @@ public MCRORCIDUser(MCRUser user) {
this.user = user;
}
+ /**
+ * Wraps MCRUser to MCRORCIDUser.
+ *
+ * @param user the MCRUser
+ * @param legalEntityService the {@link MCRLegalEntityService} to use
+ */
+ protected MCRORCIDUser(MCRUser user, MCRLegalEntityService legalEntityService) {
+ this.user = user;
+ this.legalEntityService = legalEntityService;
+ }
+
/**
* Returns MCRUser.
*
@@ -100,11 +114,10 @@ public void addORCID(String orcid) {
if (!MCRORCIDValidationHelper.validateORCID(orcid)) {
throw new MCRORCIDException("Invalid ORCID iD");
}
- final MCRUserAttribute attribute = new MCRUserAttribute(ATTR_ORCID_ID, orcid);
- // allow more than one ORCID iD per user
- if (!user.getAttributes().contains(attribute)) {
- user.getAttributes().add(new MCRUserAttribute(ATTR_ORCID_ID, orcid));
- }
+ final MCRIdentifier newOrcid = new MCRIdentifier("orcid", orcid);
+ final MCRIdentifier userid = new MCRIdentifier("userid", user.getUserID());
+
+ legalEntityService.addIdentifier(userid, newOrcid);
}
/** Returns user's ORCID iDs.
@@ -112,9 +125,10 @@ public void addORCID(String orcid) {
* @return ORCID iDs as set
*/
public Set getORCIDs() {
- return user.getAttributes().stream()
- .filter(a -> Objects.equals(a.getName(), ATTR_ORCID_ID))
- .map(MCRUserAttribute::getValue).collect(Collectors.toSet());
+ final MCRIdentifier userid = new MCRIdentifier("userid", user.getUserID());
+ Set orcidIdentifiers = legalEntityService.findAllIdentifiers(userid);
+ return orcidIdentifiers.stream().filter(identifiers -> identifiers.getType().equals("orcid"))
+ .map(MCRIdentifier::getValue).collect(Collectors.toSet());
}
/**
@@ -232,9 +246,8 @@ public MCRORCIDCredential getCredentialByORCID(String orcid) {
* @return Set of MCRIdentifier
*/
public Set getIdentifiers() {
- return user.getAttributes().stream().filter(a -> a.getName().startsWith(ATTR_ID_PREFIX))
- .map(a -> new MCRIdentifier(a.getName().substring(ATTR_ID_PREFIX.length()), a.getValue()))
- .collect(Collectors.toSet());
+ final MCRIdentifier userId = new MCRIdentifier("userid", user.getUserID());
+ return legalEntityService.findAllIdentifiers(userId);
}
/**
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java
index 569fee157f..b702899f6f 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java
@@ -23,6 +23,7 @@
import java.util.Set;
import java.util.stream.Collectors;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.mods.MCRMODSWrapper;
import org.mycore.orcid2.MCRORCIDUtils;
@@ -30,7 +31,6 @@
import org.mycore.orcid2.client.exception.MCRORCIDRequestException;
import org.mycore.orcid2.exception.MCRORCIDException;
import org.mycore.orcid2.oauth.MCRORCIDOAuthClient;
-import org.mycore.orcid2.util.MCRIdentifier;
import org.mycore.user2.MCRUser;
import org.mycore.user2.MCRUserManager;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java
index 10eca0f8ce..f6b52e6977 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java
@@ -23,7 +23,9 @@
/**
* Class to store IDs.
+ * @deprecated moved to mycore-base / org.mycore.datamodel.legalentity
*/
+@Deprecated
public class MCRIdentifier {
private final String type;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java
index 3919ae7618..e003744463 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java
@@ -27,6 +27,7 @@
import org.mycore.common.MCRSystemUserInformation;
import org.mycore.common.MCRUserInformation;
import org.mycore.common.content.MCRJDOMContent;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.datamodel.metadata.MCRMetadataManager;
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.datamodel.metadata.MCRObjectID;
@@ -39,7 +40,6 @@
import org.mycore.orcid2.user.MCRORCIDSessionUtils;
import org.mycore.orcid2.user.MCRORCIDUser;
import org.mycore.orcid2.user.MCRORCIDUserUtils;
-import org.mycore.orcid2.util.MCRIdentifier;
import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper;
import org.mycore.orcid2.v3.work.MCRORCIDWorkService;
import org.mycore.orcid2.v3.work.MCRORCIDWorkUtils;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java
index 3438e90538..ef532ec27a 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java
@@ -23,7 +23,7 @@
import org.mycore.common.content.MCRJDOMContent;
import org.mycore.orcid2.client.MCRORCIDCredential;
import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper;
import org.mycore.orcid2.work.MCRORCIDWorkEventHandler;
import org.orcid.jaxb.model.v3.release.record.Work;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java
index 14e1191462..ef2bad670c 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java
@@ -42,7 +42,7 @@
import org.mycore.orcid2.metadata.MCRORCIDMetadataUtils;
import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo;
import org.mycore.orcid2.metadata.MCRORCIDUserInfo;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.orcid2.v3.client.MCRORCIDClientHelper;
import org.mycore.orcid2.v3.client.MCRORCIDSearchImpl;
import org.mycore.orcid2.v3.client.MCRORCIDSectionImpl;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java
index 1416cc82d4..d5589e10c9 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java
@@ -33,7 +33,7 @@
import org.mycore.orcid2.exception.MCRORCIDException;
import org.mycore.orcid2.exception.MCRORCIDTransformationException;
import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper;
import org.orcid.jaxb.model.v3.release.record.summary.WorkSummary;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java
index c3c1526a85..cc38fd2aaa 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java
@@ -32,7 +32,7 @@
import org.mycore.orcid2.MCRORCIDUtils;
import org.mycore.orcid2.exception.MCRORCIDException;
import org.mycore.orcid2.exception.MCRORCIDTransformationException;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper;
import org.orcid.jaxb.model.common.Relationship;
import org.orcid.jaxb.model.v3.release.common.Contributor;
diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java
index 4fa10df154..d80e075aff 100644
--- a/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java
+++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java
@@ -53,7 +53,7 @@
import org.mycore.orcid2.user.MCRORCIDUser;
import org.mycore.orcid2.user.MCRORCIDUserProperties;
import org.mycore.orcid2.user.MCRORCIDUserUtils;
-import org.mycore.orcid2.util.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
import org.mycore.user2.MCRUser;
import org.orcid.jaxb.model.message.ScopeConstants;
diff --git a/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java b/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java
index c912b2ba28..93287050b4 100644
--- a/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java
+++ b/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java
@@ -18,53 +18,131 @@
package org.mycore.orcid2.user;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRLegalEntityService;
import org.mycore.orcid2.client.MCRORCIDCredential;
+import org.mycore.orcid2.exception.MCRORCIDException;
import org.mycore.test.MCRJPAExtension;
import org.mycore.test.MyCoReTest;
import org.mycore.user2.MCRUser;
-import org.mycore.user2.MCRUserManager;
+import org.mycore.user2.MCRUserAttribute;
+import org.mycore.user2.MCRUserIdentifierService;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
@MyCoReTest
@ExtendWith(MCRJPAExtension.class)
public class MCRORCIDUserTest {
private static final String ORCID = "0000-0001-2345-6789";
+ private static final String ORCID_2 = "0000-0002-2345-6789";
+ private static final String ORCID_3 = "0000-0003-2345-6789";
private static final String ACCESS_TOKEN = "accessToken";
+ private static MCRORCIDUser orcidUser;
+
+ private static MCRUser userMock;
+
+ @BeforeEach
+ public void prepare() {
+ userMock = new MCRUser("junit");
+ MCRLegalEntityService legalEntityServiceMock = Mockito.mock(MCRUserIdentifierService.class);
+ orcidUser = new MCRORCIDUser(userMock, legalEntityServiceMock);
+
+ when(legalEntityServiceMock.findAllIdentifiers(any(MCRIdentifier.class))).thenAnswer(
+ invocation -> userMock.getAttributes()
+ .stream().map(attr -> new MCRIdentifier(stripPrefix(attr.getName()), attr.getValue()))
+ .collect(Collectors.toSet()));
+
+ doAnswer(invocation -> {
+ MCRIdentifier identifierToAdd = invocation.getArgument(1);
+ userMock.getAttributes().add(new MCRUserAttribute(
+ "id_" + identifierToAdd.getType(), identifierToAdd.getValue()));
+ return null;
+ }).when(legalEntityServiceMock).addIdentifier(any(MCRIdentifier.class), any(MCRIdentifier.class));
+ }
+
@Test
public void testStoreGetCredentials() {
- MCRUser user = new MCRUser("junit");
- MCRUserManager.createUser(user);
- MCRORCIDUser orcidUser = new MCRORCIDUser(user);
assertEquals(0, orcidUser.getCredentials().size());
final MCRORCIDCredential credential = new MCRORCIDCredential(ACCESS_TOKEN);
orcidUser.addCredential(ORCID, credential);
// id_orcid + orcid_credential_orcid
- assertEquals(2, user.getAttributes().size());
- assertNotNull(user.getUserAttribute("orcid_credential_" + ORCID));
- assertEquals(ORCID, user.getUserAttribute("id_orcid"));
+ assertEquals(2, userMock.getAttributes().size());
+ assertNotNull(userMock.getUserAttribute("orcid_credential_" + ORCID));
+ assertEquals(ORCID, userMock.getUserAttribute("id_orcid"));
assertEquals(credential, orcidUser.getCredentialByORCID(ORCID));
}
@Test
public void testRemoveAllCredentials() {
- MCRUser user = new MCRUser("junit");
- MCRUserManager.createUser(user);
- MCRORCIDUser orcidUser = new MCRORCIDUser(user);
final MCRORCIDCredential credential = new MCRORCIDCredential(ACCESS_TOKEN);
orcidUser.addCredential(ORCID, credential);
- user.setUserAttribute("test", "test");
+ userMock.setUserAttribute("test", "test");
orcidUser.removeAllCredentials();
// id_orcid + test
- assertEquals(2, user.getAttributes().size());
- assertEquals(ORCID, user.getUserAttribute("id_orcid"));
- assertEquals("test", user.getUserAttribute("test"));
+ assertEquals(2, userMock.getAttributes().size());
+ assertEquals(ORCID, userMock.getUserAttribute("id_orcid"));
+ assertEquals("test", userMock.getUserAttribute("test"));
}
+ @Test
+ public void testAddInvalidCredentials() {
+ assertEquals(0, orcidUser.getCredentials().size());
+ final MCRORCIDCredential credential = new MCRORCIDCredential(null);
+ assertThrows(MCRORCIDException.class, () -> orcidUser.addCredential(ORCID, credential));
+ assertEquals(0, orcidUser.getCredentials().size());
+ }
+
+ @Test
+ public void testGetORCIDs() {
+ userMock.setUserAttribute("test", "test");
+ userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID));
+ userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2));
+ userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_3));
+ Set orcids = orcidUser.getORCIDs();
+ assertEquals(Set.of(ORCID, ORCID_2, ORCID_3), orcids);
+ }
+
+ @Test
+ public void testAddORCID() {
+ assertEquals(0, orcidUser.getORCIDs().size());
+ orcidUser.addORCID(ORCID);
+ assertEquals(1, orcidUser.getORCIDs().size());
+ assertEquals(Set.of(ORCID), orcidUser.getORCIDs());
+ }
+
+ @Test
+ public void testAddInvalidORCID() {
+ assertEquals(0, orcidUser.getORCIDs().size());
+ assertThrows(MCRORCIDException.class, () -> orcidUser.addORCID("1234"));
+ assertEquals(0, orcidUser.getORCIDs().size());
+ }
+
+ @Test
+ public void testGetIdentifiers() {
+ userMock.setUserAttribute("test", "test");
+ userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID));
+ Set identifiers = orcidUser.getIdentifiers();
+ assertEquals(Set.of(new MCRIdentifier("test", "test"),
+ new MCRIdentifier("orcid", ORCID)), identifiers);
+ }
+
+ private String stripPrefix(String name) {
+ return name.startsWith(MCRORCIDUser.ATTR_ID_PREFIX) ?
+ name.substring(MCRORCIDUser.ATTR_ID_PREFIX.length()) : name;
+ }
}
diff --git a/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java b/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java
new file mode 100644
index 0000000000..f13cb23fe2
--- /dev/null
+++ b/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java
@@ -0,0 +1,90 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.user2;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.datamodel.legalentity.MCRLegalEntityService;
+
+/**
+ * This class identifies {@link MCRUser users} by their user ID and looks up their identifiers through the
+ * {@link MCRUserAttribute attributes} attached to the user entity. New attributes are also persisted in the user
+ * entity.
+ */
+public class MCRUserIdentifierService implements MCRLegalEntityService {
+
+ public static final String ATTR_ID_PREFIX = "id_";
+
+ /**
+ * Gets all {@link MCRIdentifier MCRIdentifiers} of a user by its {@link MCRUser#getUserID() user ID}.
+ * @param userId the user id
+ * @return all identifiers with the prefix {@link MCRUserIdentifierService#ATTR_ID_PREFIX}
+ * or an empty set. prefix is stripped
+ */
+ @Override
+ public Set findAllIdentifiers(MCRIdentifier userId) {
+ return findUserByUserID(userId)
+ .map(user -> user.getAttributes().stream()
+ .filter(a -> a.getName().startsWith(ATTR_ID_PREFIX))
+ .map(a -> new MCRIdentifier(stripPrefix(a.getName()), a.getValue()))
+ .collect(Collectors.toSet()))
+ .orElse(Collections.emptySet());
+ }
+
+ /**
+ * Adds an attribute to a user by its {@link MCRUser#getUserID() user ID}.
+ * @param userId the user id
+ * @param attributeToAdd the attribute to add in the form of a {@link MCRIdentifier}
+ */
+ @Override
+ public void addIdentifier(MCRIdentifier userId, MCRIdentifier attributeToAdd) {
+ findUserByUserID(userId).ifPresent(user -> {
+ MCRUserAttribute newAttribute = new MCRUserAttribute(
+ ATTR_ID_PREFIX + attributeToAdd.getType(), attributeToAdd.getValue());
+ if (user.getAttributes().add(newAttribute)) {
+ MCRUserManager.updateUser(user);
+ }
+ });
+ }
+
+ /**
+ * Takes a user id and returns an Optional with the corresponding user.
+ * @param userId the user id
+ * @return a nullable Optional that might contain a user
+ */
+ private Optional findUserByUserID(MCRIdentifier userId) {
+ if (userId == null) {
+ return Optional.empty();
+ }
+ if (!"userid".equals(userId.getType())) {
+ return Optional.empty();
+ }
+ MCRUser user = MCRUserManager.getUser(userId.getValue());
+ return Optional.ofNullable(user);
+ }
+
+ private String stripPrefix(String name) {
+ return name.startsWith(ATTR_ID_PREFIX) ? name.substring(ATTR_ID_PREFIX.length()) : name;
+ }
+
+}
diff --git a/mycore-user2/src/main/resources/components/user2/config/mycore.properties b/mycore-user2/src/main/resources/components/user2/config/mycore.properties
index 2876522eea..ae412c5af5 100644
--- a/mycore-user2/src/main/resources/components/user2/config/mycore.properties
+++ b/mycore-user2/src/main/resources/components/user2/config/mycore.properties
@@ -118,3 +118,9 @@ MCR.User.PasswordCheck.SelectedStrategy=pbkdf2
##############################################################################
# can be used to persist transient user (shibboleth or ...)
#MCR.EventHandler.MCRObject.110.Class=org.mycore.user2.events.MCRPersistTransientUserEventHandler
+
+##############################################################################
+# LegalEntityService
+##############################################################################
+# Implementation of MCRLegalEntityService for querying UserIdentifier
+MCR.LegalEntityService.Class=org.mycore.user2.MCRUserIdentifierService
diff --git a/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java b/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java
new file mode 100644
index 0000000000..0a76be8650
--- /dev/null
+++ b/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java
@@ -0,0 +1,108 @@
+/*
+ * This file is part of *** M y C o R e ***
+ * See https://www.mycore.de/ for details.
+ *
+ * MyCoRe is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MyCoRe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MyCoRe. If not, see .
+ */
+
+package org.mycore.user2;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mycore.datamodel.legalentity.MCRIdentifier;
+import org.mycore.test.MCRJPAExtension;
+import org.mycore.test.MyCoReTest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@MyCoReTest
+@ExtendWith({ MCRJPAExtension.class, MCRUserExtension.class })
+public class MCRUserIdentifierServiceTest {
+
+ private static final String ORCID_1 = "0000-0001-2345-6789";
+
+ private static final String ORCID_2 = "0000-0002-3456-7895";
+
+ private static final String ORCID_3 = "0000-0003-4567-8985";
+
+ private static final String SCOPUS = "87654321";
+
+ MCRUserIdentifierService service;
+
+ MCRUser user;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ user = new MCRUser("john");
+ user.setRealName("John Doe");
+ user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_1));
+ user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2));
+ user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_3));
+ user.setUserAttribute("id_scopus", SCOPUS);
+ user.setUserAttribute("other", "abc");
+ MCRUserManager.createUser(user);
+ service = new MCRUserIdentifierService();
+ }
+
+ @Test
+ public final void testFindAllIdentifiers() {
+ Set allIdentifiers = service.findAllIdentifiers(
+ new MCRIdentifier("userid", user.getUserID()));
+ Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1),
+ new MCRIdentifier("orcid", ORCID_2),
+ new MCRIdentifier("orcid", ORCID_3),
+ new MCRIdentifier("scopus", SCOPUS));
+ assertEquals(expected, allIdentifiers);
+ }
+
+ @Test
+ public final void testAddIdentifier() {
+ MCRUser user1 = new MCRUser("jane");
+ user1.setRealName("Jane Doe");
+ user1.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_1));
+ user1.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2));
+ MCRUserManager.createUser(user1);
+
+ final MCRIdentifier userid = new MCRIdentifier("userid", user1.getUserID());
+
+ Set allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(2, allIdentifiers.size());
+
+ service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_1)); // don't add id twice
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(2, allIdentifiers.size());
+
+ service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_3));
+ allIdentifiers = service.findAllIdentifiers(userid);
+ assertEquals(3, allIdentifiers.size());
+ Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1),
+ new MCRIdentifier("orcid", ORCID_2),
+ new MCRIdentifier("orcid", ORCID_3));
+ assertEquals(expected, allIdentifiers);
+ }
+
+ @Test
+ public final void testNoUser() {
+ MCRIdentifier nonameUserid = new MCRIdentifier("userid", "noname");
+ Set allIdentifiers = service.findAllIdentifiers(nonameUserid);
+ assertEquals(0, allIdentifiers.size());
+
+ service.addIdentifier(nonameUserid, new MCRIdentifier("orcid", ORCID_1));
+ allIdentifiers = service.findAllIdentifiers(nonameUserid);
+ assertEquals(0, allIdentifiers.size());
+ }
+}