/**
* =============================================================================
*
* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import javax.ws.rs.core.MultivaluedMap;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.orcid.api.common.OauthAuthorizationPageHelper;
import org.orcid.integration.blackbox.api.v12.T2OAuthAPIService;
import org.orcid.integration.blackbox.api.v2.rc2.BlackBoxBaseRC2;
import org.orcid.integration.blackbox.web.SigninTest;
import org.orcid.jaxb.model.message.ScopePathType;
import org.orcid.pojo.ajaxForm.PojoUtil;
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 OauthAuthorizationPageTest extends BlackBoxBaseRC2 {
private static final String STATE_PARAM = "MyStateParam";
private static final String SCOPES = "/activities/update /read-limited";
private static final Pattern AUTHORIZATION_CODE_PATTERN = Pattern.compile("code=(.*)");
private static final Pattern STATE_PARAM_PATTERN = Pattern.compile("state=(.+)");
@Resource(name = "t2OAuthClient")
private T2OAuthAPIService<ClientResponse> t2OAuthClient;
@Before
public void before() {
BBBUtil.logUserOut(getWebBaseUrl(), webDriver);
}
@Test
public void stateParamIsPersistentAndReturnedOnLoginTest() throws JSONException, InterruptedException, URISyntaxException {
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), SCOPES, STATE_PARAM, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
Matcher matcher = STATE_PARAM_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String stateParam = matcher.group(1);
assertFalse(PojoUtil.isEmpty(stateParam));
assertEquals(STATE_PARAM, stateParam);
}
@Test
public void stateParamIsPersistentAndReurnedWhenAlreadyLoggedInTest() throws JSONException, InterruptedException, URISyntaxException {
signout();
signin();
String currentUrl = OauthAuthorizationPageHelper.authorizeOnAlreadyLoggedInUser(webDriver, this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), "/read-limited /funding/update /orcid-works/update /orcid-bio/external-identifiers/create", STATE_PARAM);
Matcher matcher = STATE_PARAM_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String stateParam = matcher.group(1);
assertFalse(PojoUtil.isEmpty(stateParam));
assertEquals(STATE_PARAM, stateParam);
}
@Test
public void invalidRedirectUriAllowsLoginThenShowErrorTest() throws InterruptedException {
String invalidRedirectUri = "http://www.orcid.org/worng/redirect/uri";
String formattedAuthorizationScreen = String.format(OauthAuthorizationPageHelper.authorizationScreenUrl, this.getWebBaseUrl(), this.getClient1ClientId(), SCOPES, invalidRedirectUri);
formattedAuthorizationScreen += "&state=" + STATE_PARAM;
formattedAuthorizationScreen += "#show_login";
webDriver.get(formattedAuthorizationScreen);
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
BBBUtil.extremeWaitFor(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[@name='userId']")), webDriver);
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
By userIdElementLocator = By.id("userId");
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS)).until(ExpectedConditions.presenceOfElementLocated(userIdElementLocator));
WebElement userIdElement = webDriver.findElement(userIdElementLocator);
userIdElement.sendKeys(this.getUser1UserName());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
WebElement passwordElement = webDriver.findElement(By.id("password"));
passwordElement.sendKeys(this.getUser1Password());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
BBBUtil.ngAwareClick( webDriver.findElement(By.id("login-authorize-button")), webDriver);
BBBUtil.extremeWaitFor(BBBUtil.angularHasFinishedProcessing(), webDriver);
BBBUtil.extremeWaitFor(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return d.getCurrentUrl().contains("/oauth/error/redirect-uri-mismatch");
}
}, webDriver);
String currentUrl = webDriver.getCurrentUrl();
assertTrue("URL is:" + currentUrl, currentUrl.contains("/oauth/error/redirect-uri-mismatch"));
assertTrue("URL is:" + currentUrl, currentUrl.contains("client_id=" + this.getClient1ClientId()));
assertTrue("URL is:" + currentUrl, currentUrl.contains("response_type=code"));
assertTrue("URL is:" + currentUrl, currentUrl.contains("redirect_uri=" + invalidRedirectUri));
assertTrue("URL is:" + currentUrl, currentUrl.contains("scope="));
}
@Test
public void useAuthorizationCodeWithInalidScopesTest() throws InterruptedException, JSONException {
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.ORCID_WORKS_CREATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
Matcher matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
ClientResponse tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.ORCID_WORKS_UPDATE.getContent(), this.getClient1RedirectUri(),
authorizationCode);
assertEquals(400, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject result = new JSONObject(body);
assertNotNull(result);
assertEquals("invalid_scope", result.get("error"));
assertEquals("Invalid scopes: /orcid-works/update available scopes for this code are: [/orcid-works/create]", result.get("error_description"));
}
@Test
public void useAuthorizationCodeWithoutScopesTest() throws InterruptedException, JSONException {
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.ORCID_WORKS_CREATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
Matcher matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
ClientResponse tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), null, this.getClient1RedirectUri(), authorizationCode);
assertEquals(200, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject jsonObject = new JSONObject(body);
String accessToken = (String) jsonObject.get("access_token");
assertNotNull(accessToken);
assertFalse(PojoUtil.isEmpty(accessToken));
}
@Test
public void dontSkipAuthorizationScreenIfShortTokenAlreadyExists() throws InterruptedException, JSONException {
// clean up any token lying around
BBBUtil.revokeApplicationsAccess(webDriver);
// get the authorization code
signout();
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE.value(), null, this.getUser1UserName(), this.getUser1Password(), false, webDriver);
Matcher matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
ClientResponse tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE.getContent(), this.getClient1RedirectUri(),
authorizationCode);
assertEquals(200, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject jsonObject = new JSONObject(body);
String accessToken = (String) jsonObject.get("access_token");
assertNotNull(accessToken);
assertFalse(PojoUtil.isEmpty(accessToken));
// Then, ask again for the same permissions.
//First login
webDriver.get(this.getWebBaseUrl() + "/userStatus.json?logUserOut=true");
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
webDriver.get(this.getWebBaseUrl() + "/my-orcid");
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
SigninTest.signIn(webDriver, this.getUser1UserName(), this.getUser1Password());
//Then ask for the same permission
String url =String.format("%s/oauth/authorize?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", this.getWebBaseUrl(), this.getClient1ClientId(),
ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE.getContent(), this.getClient1RedirectUri());
webDriver.get(url);
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
BBBUtil.extremeWaitFor(ExpectedConditions.presenceOfElementLocated(By.xpath("//p[contains(text(),'has asked for the following access to your ORCID Record')]")), webDriver);
}
@Test
public void skipAuthorizationScreenIfTokenLongLifeAlreadyExists() throws InterruptedException, JSONException {
// First get the authorization code
signout();
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.ORCID_BIO_UPDATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
Matcher matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
ClientResponse tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.ORCID_BIO_UPDATE.getContent(), this.getClient1RedirectUri(),
authorizationCode);
assertEquals(200, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject jsonObject = new JSONObject(body);
String accessToken = (String) jsonObject.get("access_token");
assertNotNull(accessToken);
assertFalse(PojoUtil.isEmpty(accessToken));
// Then, ask again for the same permissions.
//First login
webDriver.get(this.getWebBaseUrl() + "/userStatus.json?logUserOut=true");
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
webDriver.get(this.getWebBaseUrl() + "/my-orcid");
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.angularHasFinishedProcessing());
SigninTest.signIn(webDriver, this.getUser1UserName(), this.getUser1Password());
//Then ask for the same permission
String url =String.format("%s/oauth/authorize?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", this.getWebBaseUrl(), this.getClient1ClientId(),
ScopePathType.ORCID_BIO_UPDATE.getContent(), this.getClient1RedirectUri());
webDriver.get(url);
(new WebDriverWait(webDriver, BBBUtil.TIMEOUT_SECONDS, BBBUtil.SLEEP_MILLISECONDS)).until(BBBUtil.documentReady());
BBBUtil.extremeWaitFor(ExpectedConditions.presenceOfElementLocated(By.xpath("//title[contains(text(),'ORCID Playground')]")), webDriver);
currentUrl = webDriver.getCurrentUrl();
matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.ORCID_BIO_UPDATE.getContent(), this.getClient1RedirectUri(), authorizationCode);
assertEquals(200, tokenResponse.getStatus());
body = tokenResponse.getEntity(String.class);
jsonObject = new JSONObject(body);
String otherAccessToken = (String) jsonObject.get("access_token");
assertNotNull(otherAccessToken);
assertFalse(PojoUtil.isEmpty(otherAccessToken));
}
/**
* Test that asking for different scopes generates different tokens
*
* IMPORTANT NOTE: For this test to run, the user should not have tokens for
* any of the following scopes: - FUNDING_CREATE - AFFILIATIONS_CREATE -
* ORCID_WORKS_UPDATE
* */
@Test
public void testDifferentScopesGeneratesDifferentAccessTokens() throws InterruptedException, JSONException {
// First get the authorization code
signout();
String currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.FUNDING_CREATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
Matcher matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
String authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
ClientResponse tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.FUNDING_CREATE.getContent(), this.getClient1RedirectUri(),
authorizationCode);
assertEquals(200, tokenResponse.getStatus());
String body = tokenResponse.getEntity(String.class);
JSONObject jsonObject = new JSONObject(body);
String accessToken = (String) jsonObject.get("access_token");
assertNotNull(accessToken);
assertFalse(PojoUtil.isEmpty(accessToken));
signout();
// Then, ask again for permissions over other scopes.
currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.AFFILIATIONS_CREATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.AFFILIATIONS_CREATE.getContent(), this.getClient1RedirectUri(), authorizationCode);
assertEquals(200, tokenResponse.getStatus());
body = tokenResponse.getEntity(String.class);
jsonObject = new JSONObject(body);
String otherAccessToken = (String) jsonObject.get("access_token");
assertNotNull(otherAccessToken);
assertFalse(PojoUtil.isEmpty(otherAccessToken));
assertFalse(otherAccessToken.equals(accessToken));
signout();
currentUrl = OauthAuthorizationPageHelper.loginAndAuthorize(this.getWebBaseUrl(), this.getClient1ClientId(), this.getClient1RedirectUri(), ScopePathType.ORCID_WORKS_UPDATE.value(), null, this.getUser1UserName(), this.getUser1Password(), true, webDriver);
matcher = AUTHORIZATION_CODE_PATTERN.matcher(currentUrl);
assertTrue(matcher.find());
authorizationCode = matcher.group(1);
assertFalse(PojoUtil.isEmpty(authorizationCode));
tokenResponse = getClientResponse(this.getClient1ClientId(), this.getClient1ClientSecret(), ScopePathType.ORCID_WORKS_UPDATE.getContent(), this.getClient1RedirectUri(), authorizationCode);
assertEquals(200, tokenResponse.getStatus());
body = tokenResponse.getEntity(String.class);
jsonObject = new JSONObject(body);
String otherAccessToken2 = (String) jsonObject.get("access_token");
assertNotNull(otherAccessToken2);
assertFalse(PojoUtil.isEmpty(otherAccessToken2));
assertFalse(otherAccessToken2.equals(accessToken));
assertFalse(otherAccessToken2.equals(otherAccessToken));
}
public ClientResponse getClientResponse(String clientId, String clientSecret, String scopes, String client1RedirectUri, String authorizationCode) {
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
params.add("grant_type", "authorization_code");
if (scopes != null) {
params.add("scope", scopes);
}
params.add("redirect_uri", this.getClient1RedirectUri());
params.add("code", authorizationCode);
return t2OAuthClient.obtainOauth2TokenPost("client_credentials", params);
}
}