/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.rest.actions.oauth;
import com.eviware.soapui.impl.rest.OAuth2Profile;
import com.eviware.soapui.support.SoapUIException;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.token.BasicOAuthToken;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.oltu.oauth2.httpclient4.HttpClient4;
import org.junit.Before;
import org.junit.Test;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.eviware.soapui.impl.rest.actions.oauth.OAuth2TestUtils.ACCESS_TOKEN;
import static com.eviware.soapui.impl.rest.actions.oauth.OAuth2TestUtils.AUTHORIZATION_CODE;
import static com.eviware.soapui.impl.rest.actions.oauth.OAuth2TestUtils.OAUTH_2_OOB_URN;
import static com.eviware.soapui.impl.rest.actions.oauth.OAuth2TestUtils.REFRESH_TOKEN;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItem;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
*
*/
public class OAuth2TokenExtractorTest {
private OAuth2TokenExtractor oAuth2TokenExtractor;
private OAuth2Parameters parameters;
private OAuth2Profile profile;
private SpyingOauthClientStub spyingOauthClientStub;
private List<String> executedScripts;
private UserBrowserFacadeStub browserFacade;
@Before
public void setUp() throws SoapUIException {
spyingOauthClientStub = new SpyingOauthClientStub();
browserFacade = new UserBrowserFacadeStub();
oAuth2TokenExtractor = new OAuth2TokenExtractor() {
@Override
protected OAuthClient getOAuthClient() {
return spyingOauthClientStub;
}
@Override
protected UserBrowserFacade getBrowserFacade() {
return browserFacade;
}
};
profile = OAuth2TestUtils.getOAuthProfileWithDefaultValues();
parameters = new OAuth2Parameters(profile);
executedScripts = new ArrayList<String>();
}
@Test
public void getsAccessTokenFromURLFragmentForImplicitGrantFlow() throws OAuthSystemException, MalformedURLException, URISyntaxException {
oAuth2TokenExtractor.extractAccessTokenForImplicitGrantFlow(parameters);
assertThat(profile.getAccessToken(), is(ACCESS_TOKEN));
}
@Test
public void getsTheAccessTokenFromJsonResponse() throws Exception {
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(profile.getAccessToken(), is(ACCESS_TOKEN));
}
@Test
public void closesBrowserWindowAfterSavingTheAccessTokenToProfile() throws Exception {
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(browserFacade.browserClosed, is(true));
}
@Test
public void getsTheAccessTokenFromUrlEncodedFormResponse() throws Exception {
profile.setOAuth2Flow(OAuth2Profile.OAuth2Flow.IMPLICIT_GRANT);
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(profile.getAccessToken(), is(ACCESS_TOKEN));
}
@Test
public void getsRefreshTokenFromResponseURI() throws Exception {
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(profile.getRefreshToken(), is(REFRESH_TOKEN));
}
@Test
public void getsTheAccessTokenFromResponseBodyInOobRequest() throws Exception {
profile.setRedirectURI(OAUTH_2_OOB_URN);
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(profile.getAccessToken(), is(ACCESS_TOKEN));
}
@Test
public void storesTheAccessTokenAfterUsingRefreshToken() throws Exception {
profile.setAccessToken("expired_token!");
profile.setRefreshToken(REFRESH_TOKEN);
oAuth2TokenExtractor.refreshAccessToken(parameters);
assertThat(profile.getAccessToken(), is(ACCESS_TOKEN));
}
@Test
public void sendsAuthorizationCodeInMessageBody() throws Exception {
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
String code = (String) OAuthUtils.decodeForm(spyingOauthClientStub.oAuthClientRequest.getBody()).get("code");
assertThat(code, is(AUTHORIZATION_CODE));
}
@Test
public void updatesProfileAccessTokenStatus() throws Exception {
final List<OAuth2Profile.AccessTokenStatus> statuses = new ArrayList<OAuth2Profile.AccessTokenStatus>();
profile.addPropertyChangeListener(OAuth2Profile.ACCESS_TOKEN_STATUS_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
statuses.add((OAuth2Profile.AccessTokenStatus) evt.getNewValue());
}
});
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(statuses.size(), is(3));
assertThat(statuses, hasItem(OAuth2Profile.AccessTokenStatus.WAITING_FOR_AUTHORIZATION));
assertThat(statuses, hasItem(OAuth2Profile.AccessTokenStatus.RECEIVED_AUTHORIZATION_CODE));
assertThat(statuses, hasItem(OAuth2Profile.AccessTokenStatus.RETRIEVED_FROM_SERVER));
}
@Test
public void executesJavaScripts() throws Exception {
String firstScript = "document.getElementById('okButton').click()";
String secondScript = "document.getElementById('confirmButton').click()";
profile.setAutomationJavaScripts(Arrays.asList(firstScript, secondScript));
oAuth2TokenExtractor.extractAccessTokenForAuthorizationCodeGrantFlow(parameters);
assertThat(executedScripts, hasItem(firstScript));
assertThat(executedScripts, hasItem(secondScript));
}
private class UserBrowserFacadeStub implements UserBrowserFacade {
private List<BrowserListener> listeners = new ArrayList<BrowserListener>();
private boolean browserClosed;
@Override
public void open(URL url) {
String queryString = url.getQuery();
for (BrowserListener listener : listeners) {
listener.contentChanged("<html><body>mock_login_screen_content</body></html>");
if (queryString.contains("response_type=code")) {
if (!queryString.contains("redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob")) {
String redirectURI = getRedirectURI(queryString);
listener.locationChanged("consent_screen");
listener.contentChanged("<html><body>mock_consent_screen_content</body></html>");
listener.locationChanged(redirectURI + "?code=" + AUTHORIZATION_CODE + "&state=foo");
} else {
listener.contentChanged("<TITLE>code=" + AUTHORIZATION_CODE + "</TITLE>");
}
} else if (queryString.contains("response_type=token")) {
String redirectURI = getRedirectURI(queryString);
listener.locationChanged(redirectURI + "#access_token=" + ACCESS_TOKEN + "&refresh_token=" + REFRESH_TOKEN
+ "&expires_in=3600");
}
}
}
private String getRedirectURI(String queryString) {
String[] parameters = queryString.split("&");
for (String parameter : parameters) {
String prefix = "redirect_uri=";
if (parameter.startsWith(prefix)) {
return parameter.substring(prefix.length());
}
}
return null;
}
@Override
public void addBrowserListener(BrowserListener listener) {
listeners.add(listener);
}
@Override
public void removeBrowserStateListener(BrowserListener listener) {
listeners.remove(listener);
}
@Override
public void close() {
browserClosed = true;
}
@Override
public void executeJavaScript(String script) {
executedScripts.add(script);
}
}
private class SpyingOauthClientStub extends OAuthClient {
OAuthClientRequest oAuthClientRequest;
public SpyingOauthClientStub() {
super(new HttpClient4());
}
@Override
public <T extends OAuthAccessTokenResponse> T accessToken(OAuthClientRequest request, Class<T> responseClass)
throws OAuthSystemException, OAuthProblemException {
oAuthClientRequest = request;
OAuthAccessTokenResponse response = mock(responseClass);
when(response.getOAuthToken()).thenReturn(new BasicOAuthToken(ACCESS_TOKEN, 3600L, REFRESH_TOKEN, "user"));
return (T) response;
}
}
}