mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 18:14:26 +00:00
[CST-5669] Migrated OrcidRestController
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* 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/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.dspace.app.orcid.client.OrcidClient;
|
||||||
|
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
||||||
|
import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.rest.model.RestModel;
|
||||||
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rest controller that store ORCID infos and handles redirect.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/api/" + RestModel.EPERSON + "/orcid")
|
||||||
|
@RestController
|
||||||
|
public class OrcidRestController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrcidClient orcidClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrcidSynchronizationService orcidSynchronizationService;
|
||||||
|
|
||||||
|
@GetMapping(value = "/{itemId}")
|
||||||
|
public void linkProfileFromCode(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
@RequestParam(name = "code") String code,
|
||||||
|
@PathVariable(name = "itemId") String itemId,
|
||||||
|
@RequestParam(name = "url") String url) throws Exception {
|
||||||
|
|
||||||
|
Context context = ContextUtil.obtainContext(request);
|
||||||
|
|
||||||
|
ResearcherProfile profile = findResearcherProfile(context, UUIDUtils.fromString(itemId))
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("No profile found by item id: " + itemId));
|
||||||
|
|
||||||
|
OrcidTokenResponseDTO token = orcidClient.getAccessToken(code);
|
||||||
|
|
||||||
|
orcidSynchronizationService.linkProfile(context, profile.getItem(), token);
|
||||||
|
|
||||||
|
context.complete();
|
||||||
|
|
||||||
|
URI dspaceUiUrl = new URI(configurationService.getProperty("dspace.ui.url"));
|
||||||
|
response.sendRedirect(dspaceUiUrl.resolve(url).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ResearcherProfile> findResearcherProfile(Context context, UUID itemId) throws SQLException {
|
||||||
|
return Optional.ofNullable(itemService.find(context, itemId))
|
||||||
|
.map(ResearcherProfile::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrcidClient getOrcidClient() {
|
||||||
|
return orcidClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrcidClient(OrcidClient orcidClient) {
|
||||||
|
this.orcidClient = orcidClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,263 @@
|
|||||||
|
/**
|
||||||
|
* 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/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static org.dspace.app.matcher.MetadataValueMatcher.with;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.dspace.app.orcid.client.OrcidClient;
|
||||||
|
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
||||||
|
import org.dspace.app.rest.model.RestModel;
|
||||||
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.builder.CollectionBuilder;
|
||||||
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.EPersonBuilder;
|
||||||
|
import org.dspace.builder.ItemBuilder;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link OrcidRestController}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OrcidRestControllerIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
private final static String ORCID = "0000-1111-2222-3333";
|
||||||
|
private final static String CODE = "123456";
|
||||||
|
|
||||||
|
private final static String ACCESS_TOKEN = "c41e37e5-c2de-4177-91d6-ed9e9d1f31bf";
|
||||||
|
private final static String REFRESH_TOKEN = "0062a9eb-d4ec-4d94-9491-95dd75376d3e";
|
||||||
|
private final static String[] ORCID_SCOPES = { "FirstScope", "SecondScope" };
|
||||||
|
@Autowired
|
||||||
|
private OrcidClient orcidClient;
|
||||||
|
|
||||||
|
private OrcidClient orcidClientMock = mock(OrcidClient.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrcidRestController orcidRestController;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
private Collection profileCollection;
|
||||||
|
|
||||||
|
private EPerson user;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
orcidRestController.setOrcidClient(orcidClientMock);
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
user = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withPassword(password)
|
||||||
|
.withEmail("test@user.it")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
profileCollection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Persons")
|
||||||
|
.withEntityType("Person")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws Exception {
|
||||||
|
orcidRestController.setOrcidClient(orcidClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeProfileConfiguration() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item profileItem = ItemBuilder.createItem(context, profileCollection)
|
||||||
|
.withTitle("Test user")
|
||||||
|
.withDspaceObjectOwner("Test User", user.getID().toString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", profileItem.getID())
|
||||||
|
.param("code", CODE)
|
||||||
|
.param("url", "/home"))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrl(configurationService.getProperty("dspace.ui.url") + "/home"));
|
||||||
|
|
||||||
|
verify(orcidClientMock).getAccessToken(CODE);
|
||||||
|
verifyNoMoreInteractions(orcidClientMock);
|
||||||
|
|
||||||
|
profileItem = context.reloadEntity(profileItem);
|
||||||
|
assertThat(profileItem, notNullValue());
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("person.identifier.orcid", ORCID)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.access-token", ACCESS_TOKEN)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.refresh-token", REFRESH_TOKEN)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.scope", ORCID_SCOPES[0], 0)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.scope", ORCID_SCOPES[1], 1)));
|
||||||
|
|
||||||
|
user = context.reloadEntity(user);
|
||||||
|
assertThat(user.getNetid(), is(ORCID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeProfileConfigurationWithAnotherEPersonWithSameNetId() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item profileItem = ItemBuilder.createItem(context, profileCollection)
|
||||||
|
.withTitle("Test user")
|
||||||
|
.withDspaceObjectOwner("Test User", user.getID().toString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withPassword(password)
|
||||||
|
.withEmail("test@anotherUser.it")
|
||||||
|
.withNetId(ORCID)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", profileItem.getID())
|
||||||
|
.param("code", CODE)
|
||||||
|
.param("url", "/home"))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrl(configurationService.getProperty("dspace.ui.url") + "/home"));
|
||||||
|
|
||||||
|
verify(orcidClientMock).getAccessToken(CODE);
|
||||||
|
verifyNoMoreInteractions(orcidClientMock);
|
||||||
|
|
||||||
|
profileItem = context.reloadEntity(profileItem);
|
||||||
|
assertThat(profileItem, notNullValue());
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("person.identifier.orcid", ORCID)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.access-token", ACCESS_TOKEN)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.refresh-token", REFRESH_TOKEN)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.scope", ORCID_SCOPES[0], 0)));
|
||||||
|
assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.scope", ORCID_SCOPES[1], 1)));
|
||||||
|
|
||||||
|
user = context.reloadEntity(user);
|
||||||
|
assertThat(user.getNetid(), nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeWithoutCode() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item profileItem = ItemBuilder.createItem(context, profileCollection)
|
||||||
|
.withTitle("Test user")
|
||||||
|
.withDspaceObjectOwner("Test User", user.getID().toString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", profileItem.getID())
|
||||||
|
.param("url", "/home"))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
|
verifyNoInteractions(orcidClientMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeWithoutUrl() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item profileItem = ItemBuilder.createItem(context, profileCollection)
|
||||||
|
.withTitle("Test user")
|
||||||
|
.withDspaceObjectOwner("Test User", user.getID().toString())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", profileItem.getID())
|
||||||
|
.param("code", CODE))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
|
verifyNoInteractions(orcidClientMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeWithProfileItemNotFound() throws Exception {
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", "af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.param("code", CODE)
|
||||||
|
.param("url", "/home"))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
verifyNoInteractions(orcidClientMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinkProfileFromCodeWithInvalidProfile() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item profileItem = ItemBuilder.createItem(context, profileCollection)
|
||||||
|
.withTitle("Test user")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
when(orcidClientMock.getAccessToken(CODE)).thenReturn(buildOrcidTokenResponse(ORCID, ACCESS_TOKEN));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/" + RestModel.EPERSON + "/orcid/{itemId}", profileItem.getID())
|
||||||
|
.param("code", CODE)
|
||||||
|
.param("url", "/home"))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
|
verifyNoInteractions(orcidClientMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrcidTokenResponseDTO buildOrcidTokenResponse(String orcid, String accessToken) {
|
||||||
|
OrcidTokenResponseDTO token = new OrcidTokenResponseDTO();
|
||||||
|
token.setAccessToken(accessToken);
|
||||||
|
token.setOrcid(orcid);
|
||||||
|
token.setTokenType("Bearer");
|
||||||
|
token.setRefreshToken(REFRESH_TOKEN);
|
||||||
|
token.setName("Test User");
|
||||||
|
token.setScope(String.join(" ", ORCID_SCOPES));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user