diff --git a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java index 0006f5c01afd..bfc4ccc7594c 100644 --- a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java +++ b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java @@ -307,7 +307,6 @@ protected void createAdministrator(String email, String first, String last, ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); clarinUserRegistration.setOrganization(organization); clarinUserRegistration.setConfirmation(true); - clarinUserRegistration.setEmail(eperson.getEmail()); clarinUserRegistration.setPersonID(eperson.getID()); clarinUserRegistrationService.create(context, clarinUserRegistration); diff --git a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java index da6a70924818..3b3b9569a0eb 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -38,6 +38,9 @@ import org.dspace.authenticate.factory.AuthenticateServiceFactory; import org.dspace.authenticate.service.AuthenticationService; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.clarin.ClarinUserRegistration; +import org.dspace.content.factory.ClarinServiceFactory; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; @@ -80,6 +83,8 @@ public class LDAPAuthentication implements AuthenticationMethod { = EPersonServiceFactory.getInstance().getEPersonService(); protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); + private ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -344,6 +349,12 @@ public int authenticate(Context context, authenticationService.initEPerson(context, request, eperson); ePersonService.update(context, eperson); context.dispatchEvents(); + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization( + ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + clarinUserRegistrationService.create(context, clarinUserRegistration); context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); diff --git a/dspace-api/src/main/java/org/dspace/authenticate/OidcAuthenticationBean.java b/dspace-api/src/main/java/org/dspace/authenticate/OidcAuthenticationBean.java index 8a4ac190c816..2dbe43e70550 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/OidcAuthenticationBean.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/OidcAuthenticationBean.java @@ -26,6 +26,9 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.authenticate.oidc.OidcClient; import org.dspace.authenticate.oidc.model.OidcTokenResponseDTO; +import org.dspace.content.clarin.ClarinUserRegistration; +import org.dspace.content.factory.ClarinServiceFactory; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -64,6 +67,9 @@ public class OidcAuthenticationBean implements AuthenticationMethod { @Autowired private EPersonService ePersonService; + private ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); + @Override public boolean allowSetPassword(Context context, HttpServletRequest request, String username) throws SQLException { return false; @@ -221,6 +227,12 @@ private int registerNewEPerson(Context context, Map userInfo, St context.setCurrentUser(eperson); context.dispatchEvents(); + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization(ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + clarinUserRegistrationService.create(context, clarinUserRegistration); + return SUCCESS; } catch (Exception ex) { diff --git a/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java b/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java index a11bbfc867b4..ee1c25b6c942 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java @@ -24,6 +24,9 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.clarin.ClarinUserRegistration; +import org.dspace.content.factory.ClarinServiceFactory; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -78,6 +81,9 @@ public class OrcidAuthenticationBean implements AuthenticationMethod { @Autowired private OrcidTokenService orcidTokenService; + private ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); + @Override public int authenticate(Context context, String username, String password, String realm, HttpServletRequest request) throws SQLException { @@ -245,6 +251,12 @@ private int registerNewEPerson(Context context, Person person, OrcidTokenRespons context.setCurrentUser(eperson); context.dispatchEvents(); + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization(ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + clarinUserRegistrationService.create(context, clarinUserRegistration); + return SUCCESS; } catch (Exception ex) { diff --git a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java index d9d5338877e7..8553d467ec83 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java @@ -771,20 +771,17 @@ protected EPerson registerNewEPerson(Context context, HttpServletRequest request * Register User in the CLARIN license database * */ - // if no email the registration is postponed after entering and confirming mail - if (Objects.nonNull(email)) { - try { - ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); - clarinUserRegistration.setConfirmation(true); - clarinUserRegistration.setEmail(email); - clarinUserRegistration.setPersonID(eperson.getID()); - clarinUserRegistration.setOrganization(netid); - clarinUserRegistrationService.create(context, clarinUserRegistration); - eperson.setCanLogIn(false); - ePersonService.update(context, eperson); - } catch (Exception e) { - throw new AuthorizeException("User has not been added among registred users!"); - } + try { + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setConfirmation(true); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization( + Objects.nonNull(netid) ? netid : ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistrationService.create(context, clarinUserRegistration); + eperson.setCanLogIn(false); + ePersonService.update(context, eperson); + } catch (Exception e) { + throw new AuthorizeException("User has not been added among registred users!"); } /* CLARIN */ @@ -905,6 +902,18 @@ protected void updateEPerson(Context context, HttpServletRequest request, EPerso } ePersonService.update(context, eperson); context.dispatchEvents(); + + // Sync ClarinUserRegistration organization from the current Shibboleth netid + if (netid != null) { + List registrations = + clarinUserRegistrationService.findByEPersonUUID(context, eperson.getID()); + if (!registrations.isEmpty()) { + ClarinUserRegistration reg = registrations.get(0); + reg.setOrganization(netid); + clarinUserRegistrationService.update(context, reg); + } + } + context.restoreAuthSystemState(); } diff --git a/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java b/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java index 12dc5feda583..e2e76df91069 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java @@ -34,6 +34,9 @@ import org.dspace.authenticate.factory.AuthenticateServiceFactory; import org.dspace.authenticate.service.AuthenticationService; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.clarin.ClarinUserRegistration; +import org.dspace.content.factory.ClarinServiceFactory; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; @@ -125,6 +128,8 @@ public class X509Authentication implements AuthenticationMethod { .getAuthenticationService(); protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); + private ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -544,6 +549,11 @@ && canSelfRegister(context, request, null)) { eperson); ePersonService.update(context, eperson); context.dispatchEvents(); + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization(ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + clarinUserRegistrationService.create(context, clarinUserRegistration); context.restoreAuthSystemState(); context.setCurrentUser(eperson); request.setAttribute(X509_AUTHENTICATED, true); diff --git a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java index ba5d8cd65bfa..754ea098daee 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java @@ -745,20 +745,17 @@ protected EPerson registerNewEPerson(Context context, HttpServletRequest request * Register User in the CLARIN license database * */ - // if no email the registration is postponed after entering and confirming mail - if (Objects.nonNull(email)) { - try { - ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); - clarinUserRegistration.setConfirmation(true); - clarinUserRegistration.setEmail(email); - clarinUserRegistration.setPersonID(eperson.getID()); - clarinUserRegistration.setOrganization(org); - clarinUserRegistrationService.create(context, clarinUserRegistration); - eperson.setCanLogIn(false); - ePersonService.update(context, eperson); - } catch (Exception e) { - throw new AuthorizeException("User has not been added among registred users!") ; - } + try { + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setConfirmation(true); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization( + Objects.nonNull(org) ? org : ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistrationService.create(context, clarinUserRegistration); + eperson.setCanLogIn(false); + ePersonService.update(context, eperson); + } catch (Exception e) { + throw new AuthorizeException("User has not been added among registred users!") ; } /* CLARIN */ @@ -901,6 +898,19 @@ protected void updateEPerson(Context context, HttpServletRequest request, EPerso } ePersonService.update(context, eperson); context.dispatchEvents(); + + // Sync ClarinUserRegistration organization from the current IdP + String currentOrg = shibheaders.get_idp(); + if (currentOrg != null) { + List registrations = + clarinUserRegistrationService.findByEPersonUUID(context, eperson.getID()); + if (!registrations.isEmpty()) { + ClarinUserRegistration reg = registrations.get(0); + reg.setOrganization(currentOrg); + clarinUserRegistrationService.update(context, reg); + } + } + context.restoreAuthSystemState(); } diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistration.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistration.java index 8c8fd8def9b6..e14c58fd05bc 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistration.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistration.java @@ -48,9 +48,6 @@ public class ClarinUserRegistration implements ReloadableEntity { @Column(name = "eperson_id") private UUID ePersonID = null; - @Column(name = "email") - private String email = null; - @Column(name = "organization") private String organization = null; @@ -86,14 +83,6 @@ public Integer getID() { return id; } - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - public String getOrganization() { return organization; } diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistrationServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistrationServiceImpl.java index ae7b7bb048a3..cdd8ac1a94d0 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistrationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinUserRegistrationServiceImpl.java @@ -59,6 +59,24 @@ public ClarinUserRegistration create(Context context, "You must be an admin to create a CLARIN user registration"); } + // Prevent duplicate registrations for the same eperson_id. + // If a registration already exists for this EPerson, update it instead of creating a new one. + UUID epersonId = clarinUserRegistration.getPersonID(); + if (Objects.nonNull(epersonId)) { + List existing = clarinUserRegistrationDAO.findByEPersonUUID(context, epersonId); + if (!CollectionUtils.isEmpty(existing)) { + ClarinUserRegistration existingRegistration = existing.get(0); + log.info("ClarinUserRegistration already exists for eperson_id={}. " + + "Updating existing registration (id={}) instead of creating a duplicate.", + epersonId, existingRegistration.getID()); + // Update the existing registration with new values + existingRegistration.setOrganization(clarinUserRegistration.getOrganization()); + existingRegistration.setConfirmation(clarinUserRegistration.isConfirmation()); + clarinUserRegistrationDAO.save(context, existingRegistration); + return existingRegistration; + } + } + return clarinUserRegistrationDAO.create(context, clarinUserRegistration); } @@ -96,9 +114,9 @@ public List findByEPersonUUID(Context context, UUID eper } @Override - public List findByEmail(Context context, String email) + public List findByOrganization(Context context, String organization) throws SQLException { - return clarinUserRegistrationDAO.findByEmail(context, email); + return clarinUserRegistrationDAO.findByOrganization(context, organization); } @Override diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java index 978cabfb4bd6..58d8f2a8246a 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java @@ -22,7 +22,9 @@ import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.Site; +import org.dspace.content.clarin.ClarinUserRegistration; import org.dspace.content.dto.MetadataValueDTO; +import org.dspace.content.factory.ClarinServiceFactory; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.packager.DSpaceAIPIngester; import org.dspace.content.packager.METSManifest; @@ -31,6 +33,7 @@ import org.dspace.content.service.CollectionService; import org.dspace.content.service.ItemService; import org.dspace.content.service.SiteService; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -89,6 +92,8 @@ public class AIPTechMDCrosswalk implements IngestionCrosswalk, DisseminationCros protected final HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); protected final ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private final ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); /** * Get XML namespaces of the elements this crosswalk may return. @@ -405,6 +410,16 @@ public void ingest(Context context, DSpaceObject dso, List dimList, boo sub.setEmail(value); sub.setCanLogIn(false); ePersonService.update(context, sub); + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(sub.getID()); + clarinUserRegistration.setOrganization( + ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + try { + clarinUserRegistrationService.create(context, clarinUserRegistration); + } catch (AuthorizeException e) { + log.warn("Failed to create ClarinUserRegistration for submitter {}", value, e); + } } else { log.warn( "Ignoring unknown Submitter=" + value + " in AIP Tech MD, no matching EPerson" + diff --git a/dspace-api/src/main/java/org/dspace/content/dao/clarin/ClarinUserRegistrationDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/clarin/ClarinUserRegistrationDAO.java index 1a6a8b1be5d1..0a1aabeadaed 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/clarin/ClarinUserRegistrationDAO.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/clarin/ClarinUserRegistrationDAO.java @@ -19,5 +19,5 @@ public interface ClarinUserRegistrationDAO extends GenericDAO findByEPersonUUID(Context context, UUID epersonUUID) throws SQLException; - List findByEmail(Context context, String email) throws SQLException; + List findByOrganization(Context context, String organization) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/clarin/ClarinUserRegistrationDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/clarin/ClarinUserRegistrationDAOImpl.java index 0537a45f42ed..881f8c83a5a4 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/clarin/ClarinUserRegistrationDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/clarin/ClarinUserRegistrationDAOImpl.java @@ -36,11 +36,12 @@ public List findByEPersonUUID(Context context, UUID eper } @Override - public List findByEmail(Context context, String email) throws SQLException { + public List findByOrganization(Context context, String organization) + throws SQLException { Query query = createQuery(context, "SELECT cur FROM ClarinUserRegistration as cur " + - "WHERE cur.email = :email"); + "WHERE cur.organization = :organization"); - query.setParameter("email", email); + query.setParameter("organization", organization); query.setHint("org.hibernate.cacheable", Boolean.TRUE); return list(query); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java index 5c4cf214445e..8765cdd649fa 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java @@ -24,11 +24,14 @@ import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; +import org.dspace.content.clarin.ClarinUserRegistration; import org.dspace.content.crosswalk.CrosswalkException; +import org.dspace.content.factory.ClarinServiceFactory; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.DSpaceObjectService; +import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -60,6 +63,8 @@ public class RoleIngester implements PackageIngester { protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + private ClarinUserRegistrationService clarinUserRegistrationService = + ClarinServiceFactory.getInstance().getClarinUserRegistration(); /** * Common code to ingest roles from a Document. @@ -195,6 +200,14 @@ void ingestDocument(Context context, DSpaceObject parent, // NOTE: this update() doesn't call a commit(). So, Eperson info // may still be rolled back if a subsequent error occurs ePersonService.update(context, eperson); + if (collider == null) { + // Only create a registration for newly created EPersons, not pre-existing colliders + ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); + clarinUserRegistration.setPersonID(eperson.getID()); + clarinUserRegistration.setOrganization(ClarinUserRegistration.UNKNOWN_USER_REGISTRATION); + clarinUserRegistration.setConfirmation(false); + clarinUserRegistrationService.create(context, clarinUserRegistration); + } } // Now ingest the Groups diff --git a/dspace-api/src/main/java/org/dspace/content/service/clarin/ClarinUserRegistrationService.java b/dspace-api/src/main/java/org/dspace/content/service/clarin/ClarinUserRegistrationService.java index 0434e3f417a9..c6517ea814a5 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/clarin/ClarinUserRegistrationService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/clarin/ClarinUserRegistrationService.java @@ -25,7 +25,8 @@ ClarinUserRegistration create(Context context, List findByEPersonUUID(Context context, UUID epersonUUID) throws SQLException, AuthorizeException; - List findByEmail(Context context, String email) throws SQLException; + List findByOrganization(Context context, String organization) throws SQLException; + void delete(Context context, ClarinUserRegistration clarinUserRegistration) throws SQLException, AuthorizeException; void update(Context context, ClarinUserRegistration clarinUserRegistration) throws SQLException, AuthorizeException; } diff --git a/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java index fbc16cba90e8..f61f731b616e 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java @@ -229,7 +229,6 @@ private static int cmdAdd(Context context, String[] argv) throws AuthorizeExcept ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); clarinUserRegistration.setOrganization(command.getOptionValue(OPT_ORGANIZATION.getOpt())); clarinUserRegistration.setConfirmation(true); - clarinUserRegistration.setEmail(eperson.getEmail()); clarinUserRegistration.setPersonID(eperson.getID()); clarinUserRegistrationService.create(context, clarinUserRegistration); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.03.10__user_registration_unique_eperson_id.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.03.10__user_registration_unique_eperson_id.sql new file mode 100644 index 000000000000..6fe98cbc81f5 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.03.10__user_registration_unique_eperson_id.sql @@ -0,0 +1,12 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +CREATE UNIQUE INDEX IF NOT EXISTS user_registration_eperson_id_unique + ON user_registration (eperson_id); + +ALTER TABLE user_registration DROP COLUMN IF EXISTS email; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.03.10__user_registration_unique_eperson_id.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.03.10__user_registration_unique_eperson_id.sql new file mode 100644 index 000000000000..08f847fd296d --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.03.10__user_registration_unique_eperson_id.sql @@ -0,0 +1,13 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +CREATE UNIQUE INDEX IF NOT EXISTS user_registration_eperson_id_unique + ON user_registration (eperson_id) + WHERE eperson_id IS NOT NULL; + +ALTER TABLE user_registration DROP COLUMN IF EXISTS email; diff --git a/dspace-api/src/test/java/org/dspace/builder/ClarinUserRegistrationBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ClarinUserRegistrationBuilder.java index 87df549e08bc..8edf182e4b89 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ClarinUserRegistrationBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ClarinUserRegistrationBuilder.java @@ -47,11 +47,6 @@ public ClarinUserRegistrationBuilder withEPersonID(UUID epersonID) { return this; } - public ClarinUserRegistrationBuilder withEmail(String email) { - clarinUserRegistration.setEmail(email); - return this; - } - public ClarinUserRegistrationBuilder withOrganization(String organization) { clarinUserRegistration.setOrganization(organization); return this; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinUserRegistrationConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinUserRegistrationConverter.java index 7ac59aa1a575..a787ac090984 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinUserRegistrationConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinUserRegistrationConverter.java @@ -22,7 +22,6 @@ public ClarinUserRegistrationRest convert(ClarinUserRegistration modelObject, Pr clarinUserRegistrationRest.setProjection(projection); clarinUserRegistrationRest.setId(modelObject.getID()); clarinUserRegistrationRest.setePersonID(modelObject.getPersonID()); - clarinUserRegistrationRest.setEmail(modelObject.getEmail()); clarinUserRegistrationRest.setOrganization(modelObject.getOrganization()); clarinUserRegistrationRest.setConfirmation(modelObject.isConfirmation()); return clarinUserRegistrationRest; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClarinUserRegistrationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClarinUserRegistrationRest.java index efbe80a88b35..d507dbaac254 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClarinUserRegistrationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClarinUserRegistrationRest.java @@ -35,7 +35,6 @@ public class ClarinUserRegistrationRest extends BaseObjectRest { public static final String USER_METADATA = "userMetadata"; public UUID ePersonID; - public String email; public String organization; public boolean confirmation; @@ -50,14 +49,6 @@ public void setePersonID(UUID ePersonID) { this.ePersonID = ePersonID; } - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - public String getOrganization() { return organization; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinEPersonImportController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinEPersonImportController.java index 0bb7204fa437..3bc4a9745cc7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinEPersonImportController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinEPersonImportController.java @@ -147,60 +147,27 @@ public ClarinUserRegistrationRest importUserRegistration(HttpServletRequest requ throw new UnprocessableEntityException("Error parsing request body", e1); } - ClarinUserRegistration clarinUserRegistration = null; - boolean foundByEmail = false; - - // Check if user registration already exists by email or ePersonID - if (StringUtils.isNotBlank(userRegistrationRest.getEmail())) { - List existingRegistrations = clarinUserRegistrationService.findByEmail(context, - userRegistrationRest.getEmail()); - if (!existingRegistrations.isEmpty()) { - clarinUserRegistration = existingRegistrations.get(0); - foundByEmail = true; - } + if (Objects.isNull(userRegistrationRest.getePersonID())) { + throw new UnprocessableEntityException("Cannot import user registration: ePersonID must not be null."); } - // If not found by email, try by ePersonID - if (Objects.isNull(clarinUserRegistration) && Objects.nonNull(userRegistrationRest.getePersonID())) { - List existingRegistrationsByEPerson = - clarinUserRegistrationService.findByEPersonUUID(context, - userRegistrationRest.getePersonID()); - if (!existingRegistrationsByEPerson.isEmpty()) { - clarinUserRegistration = existingRegistrationsByEPerson.get(0); - } - } + // Look up existing registration by ePersonID + List existingRegistrationsByEPerson = + clarinUserRegistrationService.findByEPersonUUID(context, userRegistrationRest.getePersonID()); + ClarinUserRegistration clarinUserRegistration = + existingRegistrationsByEPerson.isEmpty() ? null : existingRegistrationsByEPerson.get(0); if (Objects.nonNull(clarinUserRegistration)) { - // Update existing registration if values are different - boolean needsUpdate = false; - + // Update organization and registration if the values have changed + boolean requiresUpdate = false; if (!Objects.equals(clarinUserRegistration.getOrganization(), userRegistrationRest.getOrganization())) { clarinUserRegistration.setOrganization(userRegistrationRest.getOrganization()); - needsUpdate = true; + requiresUpdate = true; } - - if (!Objects.equals(clarinUserRegistration.isConfirmation(), userRegistrationRest.isConfirmation())) { + if (clarinUserRegistration.isConfirmation() != userRegistrationRest.isConfirmation()) { clarinUserRegistration.setConfirmation(userRegistrationRest.isConfirmation()); - needsUpdate = true; + requiresUpdate = true; } - - // Do not update email if registration was matched by ePersonID instead of email - // to prevent data inconsistency - if (!Objects.equals(clarinUserRegistration.getEmail(), userRegistrationRest.getEmail())) { - if (foundByEmail) { - clarinUserRegistration.setEmail(userRegistrationRest.getEmail()); - needsUpdate = true; - } else { - // Registration found by ePersonID but email differs - potential data corruption - log.warn("User registration found by ePersonID={} has different email. " + - "Existing email='{}', incoming email='{}'. " + - "Email will NOT be updated to prevent data inconsistency.", - userRegistrationRest.getePersonID(), - clarinUserRegistration.getEmail(), - userRegistrationRest.getEmail()); - } - } - if (!Objects.equals(clarinUserRegistration.getPersonID(), userRegistrationRest.getePersonID())) { boolean canUpdate = true; @@ -211,33 +178,23 @@ public ClarinUserRegistrationRest importUserRegistration(HttpServletRequest requ if (!conflictingRegs.isEmpty() && !conflictingRegs.get(0).getID().equals(clarinUserRegistration.getID())) { // Conflict detected - log appropriate message based on how registration was found - if (foundByEmail) { - log.warn("User registration found by email='{}' has different ePersonID. " + - "Incoming ePersonID={} is already associated with another registration ID={}. " + - "ePersonID will NOT be updated to prevent data inconsistency.", - clarinUserRegistration.getEmail(), - userRegistrationRest.getePersonID(), - conflictingRegs.get(0).getID()); - } else { - log.warn("User registration found by ePersonID={} has different incoming ePersonID. " + - "Incoming ePersonID={} is already associated with another registration ID={}. " + - "ePersonID will NOT be updated to prevent data inconsistency.", - clarinUserRegistration.getPersonID(), - userRegistrationRest.getePersonID(), - conflictingRegs.get(0).getID()); - } + log.warn("User registration found by ePersonID={} has different incoming ePersonID. " + + "Incoming ePersonID={} is already associated with another registration ID={}. " + + "ePersonID will NOT be updated to prevent data inconsistency.", + clarinUserRegistration.getPersonID(), + userRegistrationRest.getePersonID(), + conflictingRegs.get(0).getID()); canUpdate = false; } } - // Update ePersonID if no conflict was detected if (canUpdate) { clarinUserRegistration.setPersonID(userRegistrationRest.getePersonID()); - needsUpdate = true; + requiresUpdate = true; } } - - if (needsUpdate) { + // Update ePersonID if no conflict was detected + if (requiresUpdate) { clarinUserRegistrationService.update(context, clarinUserRegistration); } } else { @@ -245,7 +202,6 @@ public ClarinUserRegistrationRest importUserRegistration(HttpServletRequest requ clarinUserRegistration = new ClarinUserRegistration(); clarinUserRegistration.setOrganization(userRegistrationRest.getOrganization()); clarinUserRegistration.setConfirmation(userRegistrationRest.isConfirmation()); - clarinUserRegistration.setEmail(userRegistrationRest.getEmail()); clarinUserRegistration.setPersonID(userRegistrationRest.getePersonID()); clarinUserRegistration = clarinUserRegistrationService.create(context, clarinUserRegistration); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java index 31101e97f3c7..1902bf39fcaf 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java @@ -519,7 +519,7 @@ public List processNonSignedInUser(Context context, // Get anonymous user registration - user metadata should not have `user_registration_id = null` ClarinUserRegistration clarinUserRegistration = null; List clarinUserRegistrationList = clarinUserRegistrationService - .findByEmail(context, ANONYMOUS_USER_REGISTRATION); + .findByOrganization(context, ANONYMOUS_USER_REGISTRATION); for (ClarinUserRegistration fetchedClarinuserRegistration : clarinUserRegistrationList) { if (!StringUtils.equals(fetchedClarinuserRegistration.getOrganization(), ANONYMOUS_USER_REGISTRATION)) { continue; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index b213b6d1eabb..16d2eb1ddf88 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -152,7 +152,6 @@ private EPerson createEPersonFromRestObject(Context context, EPersonRest eperson ClarinUserRegistration clarinUserRegistration = new ClarinUserRegistration(); clarinUserRegistration.setOrganization(UNKNOWN_USER_REGISTRATION); clarinUserRegistration.setConfirmation(true); - clarinUserRegistration.setEmail(eperson.getEmail()); clarinUserRegistration.setPersonID(eperson.getID()); clarinUserRegistrationService.create(context, clarinUserRegistration); } catch (SQLException e) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinEPersonImportControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinEPersonImportControllerIT.java index 956cf81c422a..6d45ca26143c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinEPersonImportControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinEPersonImportControllerIT.java @@ -75,11 +75,10 @@ private EPerson createTestEPerson(String emailSuffix, String password) throws Ex /** * Helper method to create an initial ClarinUserRegistration for testing. */ - private ClarinUserRegistration createInitialRegistration(EPerson ePerson, String email, String org) + private ClarinUserRegistration createInitialRegistration(EPerson ePerson, String org) throws Exception { return ClarinUserRegistrationBuilder .createClarinUserRegistration(context) - .withEmail(email) .withEPersonID(ePerson.getID()) .withOrganization(org) .withConfirmation(false) @@ -89,10 +88,9 @@ private ClarinUserRegistration createInitialRegistration(EPerson ePerson, String /** * Helper method to build a ClarinUserRegistrationRest import request. */ - private ClarinUserRegistrationRest buildImportRequest(String email, UUID ePersonID, String org, + private ClarinUserRegistrationRest buildImportRequest(UUID ePersonID, String org, boolean confirmation) { ClarinUserRegistrationRest request = new ClarinUserRegistrationRest(); - request.setEmail(email); request.setePersonID(ePersonID); request.setOrganization(org); request.setConfirmation(confirmation); @@ -215,7 +213,6 @@ public void createUserRegistrationTest() throws Exception { context.restoreAuthSystemState(); ClarinUserRegistrationRest userRegistrationRest = new ClarinUserRegistrationRest(); userRegistrationRest.setConfirmation(true); - userRegistrationRest.setEmail("test@test.edu"); userRegistrationRest.setePersonID(ePerson.getID()); userRegistrationRest.setOrganization("Test"); @@ -236,7 +233,6 @@ public void createUserRegistrationTest() throws Exception { ClarinUserRegistration clarinUserRegistration = clarinUserRegistrationService.find(context, idRef.get()); context.setCurrentUser(currentUser); assertTrue(clarinUserRegistration.isConfirmation()); - assertEquals(clarinUserRegistration.getEmail(), "test@test.edu"); assertEquals(clarinUserRegistration.getPersonID(), ePerson.getID()); assertEquals(clarinUserRegistration.getOrganization(), "Test"); } finally { @@ -245,83 +241,110 @@ public void createUserRegistrationTest() throws Exception { } @Test - public void updatesExistingRegistrationWhenMatchedByEmail() throws Exception { + public void updatesExistingRegistrationByEPersonID() throws Exception { context.turnOffAuthorisationSystem(); EPerson ePerson = createTestEPerson("4", "qwerty04"); - ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "user@test.edu", - "Original Org"); + ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "Original Org"); context.restoreAuthSystemState(); - // Import with same email to match by email - ClarinUserRegistrationRest request = buildImportRequest("user@test.edu", ePerson.getID(), - "Updated Org", true); + // Import with same ePersonID to match existing registration + ClarinUserRegistrationRest request = buildImportRequest(ePerson.getID(), "Updated Org", true); String authToken = getAuthToken(admin.getEmail(), password); try { performImportRequest(authToken, request); - // Verify updates were applied + // Verify organization was updated ClarinUserRegistration updated = findAsAdmin(initialRegistration.getID()); - assertEquals("user@test.edu", updated.getEmail()); assertEquals("Updated Org", updated.getOrganization()); - assertTrue(updated.isConfirmation()); } finally { ClarinUserRegistrationBuilder.deleteClarinUserRegistration(initialRegistration.getID()); } } @Test - public void preventsEmailUpdateWhenMatchedByEPersonID() throws Exception { + public void updatesOrganizationWhenMatchedByEPersonID() throws Exception { context.turnOffAuthorisationSystem(); EPerson ePerson = createTestEPerson("5", "qwerty05"); - ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "existing@test.edu", - "Original Org"); + ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "Original Org"); context.restoreAuthSystemState(); - // Import with different email - will match by ePersonID instead - ClarinUserRegistrationRest request = buildImportRequest("different@test.edu", ePerson.getID(), - "Updated Org", true); + // Import with same ePersonID and different organization + ClarinUserRegistrationRest request = buildImportRequest(ePerson.getID(), "Updated Org", true); String authToken = getAuthToken(admin.getEmail(), password); try { performImportRequest(authToken, request); - // Verify email was NOT updated but other fields were + // Verify only organization was updated ClarinUserRegistration updated = findAsAdmin(initialRegistration.getID()); - assertEquals("existing@test.edu", updated.getEmail()); // Email unchanged - assertEquals("Updated Org", updated.getOrganization()); // Organization updated - assertTrue(updated.isConfirmation()); // Confirmation updated + assertEquals("Updated Org", updated.getOrganization()); + assertTrue(updated.isConfirmation()); } finally { ClarinUserRegistrationBuilder.deleteClarinUserRegistration(initialRegistration.getID()); } } @Test - public void updatesRegistrationWhenBothEmailAndEPersonIDMatch() throws Exception { + public void updatesRegistrationWhenMatchedByEPersonID() throws Exception { context.turnOffAuthorisationSystem(); EPerson ePerson = createTestEPerson("6", "qwerty06"); - ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "consistent@test.edu", - "Original Org"); + ClarinUserRegistration initialRegistration = createInitialRegistration(ePerson, "Original Org"); context.restoreAuthSystemState(); - // Import with same email and ePersonID - both match the same record - ClarinUserRegistrationRest request = buildImportRequest("consistent@test.edu", ePerson.getID(), - "Updated Org", true); + // Import with same ePersonID + ClarinUserRegistrationRest request = buildImportRequest(ePerson.getID(), "Updated Org", true); String authToken = getAuthToken(admin.getEmail(), password); try { performImportRequest(authToken, request); - // Verify all fields were updated (happy path) + // Verify organization was updated ClarinUserRegistration updated = findAsAdmin(initialRegistration.getID()); - assertEquals("consistent@test.edu", updated.getEmail()); assertEquals("Updated Org", updated.getOrganization()); - assertTrue(updated.isConfirmation()); } finally { ClarinUserRegistrationBuilder.deleteClarinUserRegistration(initialRegistration.getID()); } } + @Test + public void importEpersonDoesNotCreateUserRegistration() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + EPersonRest data = new EPersonRest(); + data.setEmail("importnoregistration@example.com"); + data.setCanLogIn(true); + data.setMetadata(new MetadataRest()); + + AtomicReference idRef = new AtomicReference<>(); + String authToken = getAuthToken(admin.getEmail(), password); + + try { + getClient(authToken).perform(post("/api/clarin/import/eperson") + .content(mapper.writeValueAsBytes(data)) + .contentType(contentType) + .param("selfRegistered", "false") + .param("lastActive", "2020-01-01T00:00:00.000")) + .andExpect(status().isOk()) + .andDo(result -> idRef + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + // Verify no user registration was automatically created for the imported EPerson + EPerson currentUser = context.getCurrentUser(); + context.setCurrentUser(admin); + java.util.List registrations = + clarinUserRegistrationService.findByEPersonUUID(context, idRef.get()); + context.setCurrentUser(currentUser); + + assertEquals("Exactly one user registration should be created on EPerson import", + 1, registrations.size()); + + ClarinUserRegistration reg = registrations.get(0); + assertEquals(idRef.get(), reg.getPersonID()); + } finally { + EPersonBuilder.deleteEPerson(idRef.get()); + } + } + private String getStringFromDate(Date value) throws ParseException { DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); return df.format(value); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserRegistrationServiceImplIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserRegistrationServiceImplIT.java index c4c30bc9463e..9e14a00d9b26 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserRegistrationServiceImplIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserRegistrationServiceImplIT.java @@ -7,8 +7,15 @@ */ package org.dspace.app.rest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.builder.ClarinUserRegistrationBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.clarin.ClarinUserRegistration; import org.dspace.content.service.clarin.ClarinUserRegistrationService; import org.dspace.eperson.EPerson; @@ -36,4 +43,146 @@ public void testFind() throws Exception { .find(context, clarinUserRegistration.getID())); context.setCurrentUser(currentUser); } + + /** + * Verify that calling create() with the same eperson_id twice does not produce a duplicate row + * but instead updates the existing registration and returns it. + */ + @Test + public void createShouldUpdateExistingRegistrationForSameEPersonId() throws Exception { + context.turnOffAuthorisationSystem(); + try { + EPerson testPerson = EPersonBuilder.createEPerson(context) + .withEmail("duptest@example.com") + .withPassword("password123") + .build(); + + ClarinUserRegistration first = new ClarinUserRegistration(); + first.setPersonID(testPerson.getID()); + first.setOrganization("OrgA"); + first.setConfirmation(false); + + ClarinUserRegistration created = clarinUserRegistrationService.create(context, first); + assertNotNull("First create should return a non-null registration", created); + Integer firstId = created.getID(); + assertNotNull("Created registration should have an ID", firstId); + assertEquals("OrgA", created.getOrganization()); + + ClarinUserRegistration second = new ClarinUserRegistration(); + second.setPersonID(testPerson.getID()); + second.setOrganization("OrgB"); + second.setConfirmation(true); + + ClarinUserRegistration result = clarinUserRegistrationService.create(context, second); + assertNotNull("Second create should return a non-null registration", result); + + assertEquals("Should return the existing registration ID, not a new one", + firstId, result.getID()); + + // Organization should be updated to the new value + assertEquals("OrgB", result.getOrganization()); + assertTrue("Confirmation should be updated to true", result.isConfirmation()); + + // Verify only one row exists in the database for this eperson_id + List allForEPerson = + clarinUserRegistrationService.findByEPersonUUID(context, testPerson.getID()); + assertEquals("There must be exactly one registration for this eperson_id", + 1, allForEPerson.size()); + } finally { + context.restoreAuthSystemState(); + } + } + + /** + * Verify that creating registrations for different ePersons works + */ + @Test + public void createShouldAllowDifferentEPersonIds() throws Exception { + context.turnOffAuthorisationSystem(); + ClarinUserRegistration createdA = null; + ClarinUserRegistration createdB = null; + + try { + EPerson personA = EPersonBuilder.createEPerson(context) + .withEmail("personA@example.com") + .withPassword("password123") + .build(); + EPerson personB = EPersonBuilder.createEPerson(context) + .withEmail("personB@example.com") + .withPassword("password123") + .build(); + + ClarinUserRegistration regA = new ClarinUserRegistration(); + regA.setPersonID(personA.getID()); + regA.setOrganization("OrgA"); + regA.setConfirmation(true); + + ClarinUserRegistration regB = new ClarinUserRegistration(); + regB.setPersonID(personB.getID()); + regB.setOrganization("OrgB"); + regB.setConfirmation(true); + + createdA = clarinUserRegistrationService.create(context, regA); + createdB = clarinUserRegistrationService.create(context, regB); + + assertNotNull(createdA); + assertNotNull(createdB); + // They must be distinct rows + assertTrue("Registrations for different ePersons must have different IDs", + !createdA.getID().equals(createdB.getID())); + assertEquals("OrgA", createdA.getOrganization()); + assertEquals("OrgB", createdB.getOrganization()); + } finally { + // cleanup + if (createdA != null) { + clarinUserRegistrationService.delete(context, createdA); + } + if (createdB != null) { + clarinUserRegistrationService.delete(context, createdB); + } + context.restoreAuthSystemState(); + } + } + + /** + * Verify that creating a registration with a null eperson_id (anonymous) does not trigger + * the dedup guard and creates a new row every time. + */ + @Test + public void createShouldAllowMultipleNullEPersonIds() throws Exception { + context.turnOffAuthorisationSystem(); + + ClarinUserRegistration created1 = null; + ClarinUserRegistration created2 = null; + + try { + ClarinUserRegistration anon1 = new ClarinUserRegistration(); + anon1.setOrganization("Unknown"); + anon1.setConfirmation(true); + // personID is null by default + + ClarinUserRegistration anon2 = new ClarinUserRegistration(); + anon2.setOrganization("Unknown"); + anon2.setConfirmation(true); + + created1 = clarinUserRegistrationService.create(context, anon1); + created2 = clarinUserRegistrationService.create(context, anon2); + + assertNotNull(created1); + assertNotNull(created2); + // Both anonymous, so each should get its own row + assertTrue("Null-eperson registrations should create separate rows", + !created1.getID().equals(created2.getID())); + } finally { + // cleanup + if (created1 != null) { + clarinUserRegistrationService.delete(context, created1); + } + if (created2 != null) { + clarinUserRegistrationService.delete(context, created2); + } + + context.restoreAuthSystemState(); + } + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index e1febcfa0fe0..b9ffc6101682 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -173,8 +173,6 @@ public void createTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(1))) .andExpect(jsonPath( "$._embedded.clarinuserregistrations[0].id", is(not(empty())))) - .andExpect(jsonPath( - "$._embedded.clarinuserregistrations[0].email", is("createtest@example.com"))) .andExpect(jsonPath( "$._embedded.clarinuserregistrations[0].confirmation", is(true))) .andExpect(jsonPath( @@ -191,9 +189,6 @@ public void createTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(1))) .andExpect(jsonPath( "$._embedded.clarinuserregistrations[0].id", is(not(empty())))) - .andExpect(jsonPath( - "$._embedded.clarinuserregistrations[0].email", - is("createtestfull@example.com"))) .andExpect(jsonPath( "$._embedded.clarinuserregistrations[0].confirmation", is(true))) .andExpect(jsonPath(