/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.integration.blackbox.api.security;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.orcid.core.utils.JsonUtils;
import org.orcid.integration.api.helper.APIRequestType;
import org.orcid.integration.api.helper.OauthHelper;
import org.orcid.integration.blackbox.api.v12.T2OAuthAPIService;
import org.orcid.integration.blackbox.api.v2.release.BlackBoxBaseV2Release;
import org.orcid.jaxb.model.error_v2.OrcidError;
import org.orcid.jaxb.model.message.OrcidMessage;
import org.orcid.jaxb.model.message.OrcidProfile;
import org.orcid.jaxb.model.message.ScopePathType;
import org.orcid.jaxb.model.record_v2.Address;
import org.orcid.jaxb.model.record_v2.Education;
import org.orcid.jaxb.model.record_v2.Employment;
import org.orcid.jaxb.model.record_v2.Funding;
import org.orcid.jaxb.model.record_v2.Keyword;
import org.orcid.jaxb.model.record_v2.OtherName;
import org.orcid.jaxb.model.record_v2.PeerReview;
import org.orcid.jaxb.model.record_v2.PersonExternalIdentifier;
import org.orcid.jaxb.model.record_v2.ResearcherUrl;
import org.orcid.jaxb.model.record_v2.Work;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.core.util.MultivaluedMapImpl;
/**
*
* @author Angel Montenegro
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
public class AccessTokenSecurityChecksTest extends BlackBoxBaseV2Release {
@Resource
private OauthHelper oauthHelper;
@Resource(name = "t2OAuthClient_1_2")
protected T2OAuthAPIService<ClientResponse> t2OAuthClient_1_2;
@SuppressWarnings("unchecked")
@Test
public void testInvalidTokenResponse() throws IOException {
ClientResponse response = memberV2ApiClient.viewPerson(getUser1OrcidId(), "invalid_token");
assertNotNull(response);
assertEquals(ClientResponse.Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
InputStream stream = response.getEntityInputStream();
String result = null;
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(stream))) {
result = buffer.lines().collect(Collectors.joining("\n"));
}
assertNotNull(result);
HashMap<String, String> error = JsonUtils.readObjectFromJsonString(result, HashMap.class);
assertNotNull(error);
assertEquals("invalid_token", error.get("error"));
assertEquals("Invalid access token: invalid_token", error.get("error_description"));
}
@SuppressWarnings("unchecked")
@Test
public void testInvalidClientResponse() throws IOException {
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
params.add("client_id", "APP-0000000000000000");
params.add("client_secret", "clientSecret");
params.add("grant_type", "client_credentials");
params.add("scope", "/read-public");
ClientResponse response = oauthHelper.getResponse(params, APIRequestType.MEMBER);
assertEquals(ClientResponse.Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
String result = response.getEntity(String.class);
assertNotNull(result);
HashMap<String, String> error = JsonUtils.readObjectFromJsonString(result, HashMap.class);
assertNotNull(error);
assertEquals("invalid_client", error.get("error"));
assertEquals("Client not found: APP-0000000000000000", error.get("error_description"));
}
@Test
public void testTokenIssuedForOneUserFailForOtherUsers_12API() throws JSONException, InterruptedException, URISyntaxException {
String accessToken = getNonCachedAccessTokens(getUser2OrcidId(), getUser2Password(), getScopes(), getClient1ClientId(), getClient1ClientSecret(), getClient1RedirectUri());
String orcid = getUser1OrcidId();
OrcidMessage message = new OrcidMessage();
message.setMessageVersion(OrcidMessage.DEFAULT_VERSION);
OrcidProfile orcidProfile = new OrcidProfile();
orcidProfile.setOrcidIdentifier(orcid);
message.setOrcidProfile(orcidProfile);
// Add operations
evaluateResponseOn12API(t2OAuthClient_1_2.addAffiliationsJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addAffiliationsXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addExternalIdentifiersJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addExternalIdentifiersXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addFundingJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addFundingXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addWorksJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.addWorksXml(orcid, message, accessToken));
// Update operations
evaluateResponseOn12API(t2OAuthClient_1_2.updateAffiliationsJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateAffiliationsXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateBioDetailsJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateBioDetailsXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateFundingJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateFundingXml(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateWorksJson(orcid, message, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.updateWorksXml(orcid, message, accessToken));
// View operations
evaluateResponseOn12API(t2OAuthClient_1_2.viewAffiliationDetailsJson(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewAffiliationDetailsXml(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewBioDetailsJson(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewBioDetailsXml(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewFundingDetailsJson(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewFundingDetailsXml(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewWorksDetailsJson(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewWorksDetailsXml(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewFullDetailsJson(orcid, accessToken));
evaluateResponseOn12API(t2OAuthClient_1_2.viewFullDetailsXml(orcid, accessToken));
}
@Test
public void testTokenIssuedForOneUserFailForOtherUsers_20API() throws JSONException, InterruptedException, URISyntaxException {
String accessToken = getNonCachedAccessTokens(getUser2OrcidId(), getUser2Password(), getScopes(), getClient1ClientId(), getClient1ClientSecret(), getClient1RedirectUri());
String orcid = getUser1OrcidId();
Long putCode = 1L;
Address address = (Address) unmarshallFromPath("/record_2.0/samples/read_samples/address-2.0.xml", Address.class);
evaluateResponse(memberV2ApiClient.createAddress(orcid, address, accessToken));
Education education = (Education) unmarshallFromPath("/record_2.0/samples/read_samples/education-2.0.xml", Education.class);
evaluateResponse(memberV2ApiClient.createEducationJson(orcid, education, accessToken));
evaluateResponse(memberV2ApiClient.createEducationXml(orcid, education, accessToken));
Employment employment = (Employment) unmarshallFromPath("/record_2.0/samples/read_samples/employment-2.0.xml", Employment.class);
evaluateResponse(memberV2ApiClient.createEmploymentJson(orcid, employment, accessToken));
evaluateResponse(memberV2ApiClient.createEmploymentXml(orcid, employment, accessToken));
PersonExternalIdentifier externalIdentifier = (PersonExternalIdentifier) unmarshallFromPath("/record_2.0/samples/read_samples/external-identifier-2.0.xml",
PersonExternalIdentifier.class);
evaluateResponse(memberV2ApiClient.createExternalIdentifier(orcid, externalIdentifier, accessToken));
Funding funding = (Funding) unmarshallFromPath("/record_2.0/samples/read_samples/funding-2.0.xml", Funding.class);
evaluateResponse(memberV2ApiClient.createFundingJson(orcid, funding, accessToken));
evaluateResponse(memberV2ApiClient.createFundingXml(orcid, funding, accessToken));
Keyword keyword = (Keyword) unmarshallFromPath("/record_2.0/samples/read_samples/keyword-2.0.xml", Keyword.class);
evaluateResponse(memberV2ApiClient.createKeyword(orcid, keyword, accessToken));
OtherName otherName = (OtherName) unmarshallFromPath("/record_2.0/samples/read_samples/other-name-2.0.xml", OtherName.class);
evaluateResponse(memberV2ApiClient.createOtherName(orcid, otherName, accessToken));
PeerReview peerReview = (PeerReview) unmarshallFromPath("/record_2.0/samples/read_samples/peer-review-2.0.xml", PeerReview.class);
evaluateResponse(memberV2ApiClient.createPeerReviewJson(orcid, peerReview, accessToken));
evaluateResponse(memberV2ApiClient.createPeerReviewXml(orcid, peerReview, accessToken));
ResearcherUrl rUrl = (ResearcherUrl) unmarshallFromPath("/record_2.0/samples/read_samples/researcher-url-2.0.xml", ResearcherUrl.class);
evaluateResponse(memberV2ApiClient.createResearcherUrls(orcid, rUrl, accessToken));
Work work = (Work) unmarshallFromPath("/record_2.0/samples/read_samples/work-2.0.xml", Work.class);
evaluateResponse(memberV2ApiClient.createWorkJson(orcid, work, accessToken));
evaluateResponse(memberV2ApiClient.createWorkXml(orcid, work, accessToken));
evaluateResponse(memberV2ApiClient.deleteAddress(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteEducationXml(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteEmploymentXml(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteExternalIdentifier(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteFundingXml(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteKeyword(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteOtherName(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deletePeerReviewXml(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteResearcherUrl(orcid, putCode, accessToken));
evaluateResponse(memberV2ApiClient.deleteWorkXml(orcid, putCode, accessToken));
address.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateAddress(orcid, address, accessToken));
education.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateEducation(orcid, education, accessToken));
employment.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateEmployment(orcid, employment, accessToken));
externalIdentifier.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateExternalIdentifier(orcid, externalIdentifier, accessToken));
funding.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateFunding(orcid, funding, accessToken));
keyword.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateKeyword(orcid, keyword, accessToken));
otherName.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateOtherName(orcid, otherName, accessToken));
peerReview.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updatePeerReview(orcid, peerReview, accessToken));
rUrl.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateResearcherUrls(orcid, rUrl, accessToken));
work.setPutCode(putCode);
evaluateResponse(memberV2ApiClient.updateWork(orcid, work, accessToken));
evaluateResponse(memberV2ApiClient.getResearcherUrls(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewAddresses(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewExternalIdentifiers(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewKeywords(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewOtherNames(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewBiography(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewPersonalDetailsXML(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewActivities(orcid, accessToken));
evaluateResponse(memberV2ApiClient.viewPerson(orcid, accessToken));
}
@Test
public void invalidAuthorizationCodesFailTest() throws InterruptedException, JSONException {
String clientId = getClient1ClientId();
String clientRedirectUri = getClient1RedirectUri();
String clientSecret = getClient1ClientSecret();
String userId = getUser1OrcidId();
String password = getUser1Password();
String scope = "/orcid-works/create";
String authorizationCode = getAuthorizationCode(clientId, clientRedirectUri, scope, userId, password, true);
assertNotNull(authorizationCode);
ClientResponse tokenResponse = getAccessTokenResponse(clientId, clientSecret, clientRedirectUri, "invalid-authorization-code");
assertEquals(500, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject jsonObject = new JSONObject(body);
assertEquals("server_error", jsonObject.get("error"));
assertEquals("Invalid authorization code: invalid-authorization-code", jsonObject.get("error_description"));
}
private List<String> getScopes() {
return getScopes(ScopePathType.ACTIVITIES_READ_LIMITED, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.AFFILIATIONS_CREATE,
ScopePathType.AFFILIATIONS_READ_LIMITED, ScopePathType.AFFILIATIONS_UPDATE, ScopePathType.AUTHENTICATE, ScopePathType.FUNDING_CREATE,
ScopePathType.FUNDING_READ_LIMITED, ScopePathType.FUNDING_UPDATE, ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE,
ScopePathType.ORCID_BIO_READ_LIMITED, ScopePathType.ORCID_BIO_UPDATE, ScopePathType.ORCID_PROFILE_READ_LIMITED, ScopePathType.ORCID_WORKS_CREATE,
ScopePathType.ORCID_WORKS_READ_LIMITED, ScopePathType.ORCID_WORKS_UPDATE, ScopePathType.PEER_REVIEW_CREATE, ScopePathType.PEER_REVIEW_READ_LIMITED);
}
private void evaluateResponse(ClientResponse response) {
assertNotNull(response);
assertEquals(ClientResponse.Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
OrcidError error = response.getEntity(OrcidError.class);
assertNotNull(error);
assertEquals("401 Unauthorized: The client application is not authorized for this ORCID record.", error.getDeveloperMessage());
assertEquals(Integer.valueOf(9017), error.getErrorCode());
}
private void evaluateResponseOn12API(ClientResponse response) {
assertNotNull(response);
assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
OrcidMessage orcidMessage = response.getEntity(OrcidMessage.class);
assertNotNull(orcidMessage);
assertNotNull(orcidMessage.getErrorDesc());
assertEquals("Security problem : You do not have the required permissions.", orcidMessage.getErrorDesc().getContent());
}
}