From 11b68a018ea8e289019daccd2b5de9f676708240 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Wed, 6 Mar 2024 14:16:08 +0100 Subject: [PATCH 1/2] delete orcid sync settings when unlinking the profile --- .../impl/OrcidSynchronizationServiceImpl.java | 8 ++ .../ResearcherProfileRestRepositoryIT.java | 76 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java index 97d832d3de82..9d32d2e5ed3e 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java @@ -118,6 +118,14 @@ public void unlinkProfile(Context context, Item profile) throws SQLException { itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); + itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-mode", Item.ANY); + itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-profile", Item.ANY); + + for (OrcidEntityType entityType : OrcidEntityType.values()) { + itemService.clearMetadata(context, profile, "dspace", "orcid", + "sync-" + entityType.name().toLowerCase() + "s", Item.ANY); + } + orcidTokenService.deleteByProfileItem(context, profile); updateItem(context, profile); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index 70009d049fc5..89fb635ed974 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -20,6 +20,7 @@ import static org.dspace.app.rest.matcher.ResourcePolicyMatcher.matchResourcePolicyProperties; import static org.dspace.builder.RelationshipTypeBuilder.createRelationshipTypeBuilder; import static org.dspace.profile.OrcidEntitySyncPreference.ALL; +import static org.dspace.profile.OrcidEntitySyncPreference.DISABLED; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; @@ -2231,6 +2232,81 @@ public void testAnotherUserPatchToDisconnectProfileFromOrcidWithAdminAndOwnerCon assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); } + @Test + public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettings() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "only_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(ePerson.getEmail(), password); + String ePersonId = ePerson.getID().toString(); + List operations = asList(new ReplaceOperation("/orcid/mode", "BATCH"), + new ReplaceOperation("/orcid/publications", "DISABLED"), + new ReplaceOperation("/orcid/fundings", "ALL"), + new ReplaceOperation("/orcid/profile", "BIOGRAPHICAL,IDENTIFIERS")); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.mode", is("BATCH"))) + .andExpect(jsonPath("$.orcidSynchronization.profilePreferences", + containsInAnyOrder("IDENTIFIERS", "BIOGRAPHICAL"))) + .andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is(ALL.name()))) + .andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is(DISABLED.name()))); + + profile = context.reloadEntity(profile); + List metadata = profile.getMetadata(); + assertThat(metadata, hasItem(with("dspace.orcid.sync-mode","BATCH"))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-publications",DISABLED.name()))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-fundings",ALL.name()))); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), hasSize(2)); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(ePersonId))) + .andExpect(jsonPath("$.visible", is(false))) + .andExpect(jsonPath("$.type", is("profile"))) + .andExpect(jsonPath("$.orcid").doesNotExist()) + .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-mode"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-fundings"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-publications"), empty()); + assertThat(getOrcidAccessToken(profile), nullValue()); + } + + @Test public void testCloneFromExternalProfileAlreadyAssociated() throws Exception { From 4bb32a82f9c3e9cce7ba3efea2ef8da9d31f1cd2 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Wed, 6 Mar 2024 17:52:30 +0100 Subject: [PATCH 2/2] configurable deletion of orcid sync settings when unlinking profile --- .../impl/OrcidSynchronizationServiceImpl.java | 19 +++-- .../ResearcherProfileRestRepositoryIT.java | 77 ++++++++++++++++++- dspace/config/modules/orcid.cfg | 5 +- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java index 9d32d2e5ed3e..59e4dea64145 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java @@ -118,12 +118,8 @@ public void unlinkProfile(Context context, Item profile) throws SQLException { itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); - itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-mode", Item.ANY); - itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-profile", Item.ANY); - - for (OrcidEntityType entityType : OrcidEntityType.values()) { - itemService.clearMetadata(context, profile, "dspace", "orcid", - "sync-" + entityType.name().toLowerCase() + "s", Item.ANY); + if (!configurationService.getBooleanProperty("orcid.disconnection.remain-sync", false)) { + clearSynchronizationSettings(context, profile); } orcidTokenService.deleteByProfileItem(context, profile); @@ -275,6 +271,17 @@ private boolean updatePreferenceForSynchronizingWithOrcid(Context context, Item } + private void clearSynchronizationSettings(Context context, Item profile) + throws SQLException { + itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-mode", Item.ANY); + itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-profile", Item.ANY); + + for (OrcidEntityType entityType : OrcidEntityType.values()) { + itemService.clearMetadata(context, profile, "dspace", "orcid", + "sync-" + entityType.name().toLowerCase() + "s", Item.ANY); + } + } + private boolean containsSameValues(List firstList, List secondList) { return new HashSet<>(firstList).equals(new HashSet<>(secondList)); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index 89fb635ed974..b3c70f8128d4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -2233,9 +2233,10 @@ public void testAnotherUserPatchToDisconnectProfileFromOrcidWithAdminAndOwnerCon } @Test - public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettings() throws Exception { + public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemoved() throws Exception { configurationService.setProperty("orcid.disconnection.allowed-users", "only_owner"); + configurationService.setProperty("orcid.disconnection.remain-sync", "false"); context.turnOffAuthorisationSystem(); @@ -2306,6 +2307,80 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettings() throws assertThat(getOrcidAccessToken(profile), nullValue()); } + @Test + public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemain() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "only_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(ePerson.getEmail(), password); + String ePersonId = ePerson.getID().toString(); + List operations = asList(new ReplaceOperation("/orcid/mode", "BATCH"), + new ReplaceOperation("/orcid/publications", "DISABLED"), + new ReplaceOperation("/orcid/fundings", "ALL"), + new ReplaceOperation("/orcid/profile", "BIOGRAPHICAL,IDENTIFIERS")); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.mode", is("BATCH"))) + .andExpect(jsonPath("$.orcidSynchronization.profilePreferences", + containsInAnyOrder("IDENTIFIERS", "BIOGRAPHICAL"))) + .andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is(ALL.name()))) + .andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is(DISABLED.name()))); + + profile = context.reloadEntity(profile); + List metadata = profile.getMetadata(); + assertThat(metadata, hasItem(with("dspace.orcid.sync-mode","BATCH"))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-publications",DISABLED.name()))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-fundings",ALL.name()))); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), hasSize(2)); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(ePersonId))) + .andExpect(jsonPath("$.visible", is(false))) + .andExpect(jsonPath("$.type", is("profile"))) + .andExpect(jsonPath("$.orcid").doesNotExist()) + .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), empty()); + assertThat(metadata, hasItem(with("dspace.orcid.sync-mode","BATCH"))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-publications",DISABLED.name()))); + assertThat(metadata, hasItem(with("dspace.orcid.sync-fundings",ALL.name()))); + assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), hasSize(2)); + assertThat(getOrcidAccessToken(profile), nullValue()); + } + @Test public void testCloneFromExternalProfileAlreadyAssociated() throws Exception { diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index 5b1503034db5..28e8df60f83d 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -7,6 +7,9 @@ # Allowed values are disabled, only_admin, only_owner or admin_and_owner orcid.disconnection.allowed-users = admin_and_owner +# Configuration if the orcid sync settings should be remain on he profile when it is disconnected from orcid or not +orcid.disconnection.remain-sync = true + #------------------------------------------------------------------# #--------------------ORCID CLIENT CONFIGURATIONS-------------------# #------------------------------------------------------------------# @@ -163,4 +166,4 @@ orcid.external-data.mapping.publication.external-ids = dc.identifier.doi::doi orcid.external-data.mapping.publication.external-ids = dc.identifier.scopus::eid orcid.external-data.mapping.publication.external-ids = dc.identifier.pmid::pmid orcid.external-data.mapping.publication.external-ids = dc.identifier.isi::wosuid -orcid.external-data.mapping.publication.external-ids = dc.identifier.issn::issn \ No newline at end of file +orcid.external-data.mapping.publication.external-ids = dc.identifier.issn::issn