Here's something to try. It seems to work for me in that it doesn't give any script errors. But I really have no clue if it will have the desired effect and no way to test it.
The plan is to address the OAuth timeout which is hardcoded into a class called OAuth2RequestFilter. Luckily, Groovy scripts let us interact with SoapUI's own class instances at run time. So we define a new class that's a copy of that one, but with a different timeout. Then tell SoapUI to use our class instead through its RequestTransportRegistry.
If it does work, I think that as soon as you run this script once the change to the timeout will last until you reload SoapUI. So you could put it in the load script for your Project or something.
import com.eviware.soapui.impl.wsdl.submit.filters.AbstractRequestFilter
import com.eviware.soapui.impl.wsdl.submit.filters.OAuth2RequestFilter
import com.eviware.soapui.impl.wsdl.submit.RequestTransportRegistry
RequestTransportRegistry.getTransport("https").replaceRequestFilter(OAuth2RequestFilter, new modifiedOAuth2RequestFilter())
/*
* below is a copy of class OAuth2RequestFilter with a new name as retrieved from
* https://github.com/SmartBear/soapui/blob/next/soapui/src/main/java/com/eviware/soapui/impl/wsdl/submit/filters/OAuth2RequestFilter.java
* except that the ACCESS_TOKEN_RETRIEVAL_TIMEOUT value has been changed from 5000 to 10000
*/
import com.eviware.soapui.config.TimeUnitConfig;
import com.eviware.soapui.impl.rest.OAuth1Profile;
import com.eviware.soapui.impl.rest.OAuth1ProfileContainer;
import com.eviware.soapui.impl.rest.OAuth2Profile;
import com.eviware.soapui.impl.rest.OAuth2ProfileContainer;
import com.eviware.soapui.impl.rest.RestRequestInterface;
import com.eviware.soapui.impl.rest.actions.oauth.GoogleOAuth1ClientFacade;
import com.eviware.soapui.impl.rest.actions.oauth.OAuth1ClientFacade;
import com.eviware.soapui.impl.rest.actions.oauth.OAuth2ClientFacade;
import com.eviware.soapui.impl.rest.actions.oauth.OltuOAuth2ClientFacade;
import com.eviware.soapui.impl.support.AbstractHttpRequest;
import com.eviware.soapui.impl.wsdl.submit.transports.http.BaseHttpRequestTransport;
import com.eviware.soapui.model.iface.SubmitContext;
import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.TimeUtils;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.log4j.Logger;
import static com.eviware.soapui.config.CredentialsConfig.AuthType.O_AUTH_1_0;
import static com.eviware.soapui.config.CredentialsConfig.AuthType.O_AUTH_2_0;
public class modifiedOAuth2RequestFilter extends AbstractRequestFilter {
private static final int ACCESS_TOKEN_RETRIEVAL_TIMEOUT = 10000;
// intentionally left non-final to facilitate testing, but should not be modified in production!
private static Logger log = Logger.getLogger(OAuth2RequestFilter.class);
/* setLog() and getLog() should only be used for testing */
static Logger getLog() {
return log;
}
static void setLog(Logger newLog) {
log = newLog;
}
@Override
public void filterRestRequest(SubmitContext context, RestRequestInterface request) {
HttpRequestBase httpMethod = (HttpRequestBase) context.getProperty(BaseHttpRequestTransport.HTTP_METHOD);
if (O_AUTH_2_0.toString().equals(request.getAuthType())) {
OAuth2ProfileContainer profileContainer = request.getResource().getService().getProject()
.getOAuth2ProfileContainer();
OAuth2Profile profile = profileContainer.getProfileByName(((AbstractHttpRequest) request).getSelectedAuthProfile());
if (profile == null || StringUtils.isNullOrEmpty(profile.getAccessToken())) {
return;
}
OAuth2ClientFacade oAuth2Client = getOAuth2ClientFacade();
if (accessTokenIsExpired(profile)) {
if (profile.shouldReloadAccessTokenAutomatically()) {
reloadAccessToken(profile, oAuth2Client);
} else {
profile.setAccessTokenStatus(OAuth2Profile.AccessTokenStatus.EXPIRED);
}
}
oAuth2Client.applyAccessToken(profile, httpMethod, request.getRequestContent());
} else if (O_AUTH_1_0.toString().equals(request.getAuthType())) {
OAuth1ProfileContainer profileContainer = request.getResource().getService().getProject()
.getOAuth1ProfileContainer();
OAuth1Profile profile = profileContainer.getProfileByName(
((AbstractHttpRequest) request).getSelectedAuthProfile());
if (profile == null || StringUtils.isNullOrEmpty(profile.getAccessToken())) {
return;
}
OAuth1ClientFacade oAuth1Client = getOAuth1ClientFacade();
oAuth1Client.applyAccessToken(profile, httpMethod, request.getRequestContent());
}
}
protected OAuth2ClientFacade getOAuth2ClientFacade() {
return new OltuOAuth2ClientFacade();
}
protected OAuth1ClientFacade getOAuth1ClientFacade() {
return new GoogleOAuth1ClientFacade();
}
private boolean accessTokenIsExpired(OAuth2Profile profile) {
long currentTime = TimeUtils.getCurrentTimeInSeconds();
long issuedTime = profile.getAccessTokenIssuedTime();
long expirationTime;
if (profile.useManualAccessTokenExpirationTime()) {
String expirationTimeString = profile.getManualAccessTokenExpirationTime() == null ? "" : profile.getManualAccessTokenExpirationTime();
String expandedValue = PropertyExpander.expandProperties(profile.getContainer().getProject(), expirationTimeString);
expirationTime = convertExpirationTimeToSeconds(expandedValue, profile.getManualAccessTokenExpirationTimeUnit());
} else {
expirationTime = profile.getAccessTokenExpirationTime();
}
//10 second buffer to make sure that the access token doesn't expire by the time request is sent
return !(issuedTime <= 0 || expirationTime <= 0) && expirationTime < (currentTime + 10) - issuedTime;
}
private long convertExpirationTimeToSeconds(String expirationTimeString, TimeUnitConfig.Enum timeUnit) throws IllegalArgumentException {
long expirationTime;
try {
expirationTime = Long.valueOf(expirationTimeString.trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Manual expiration time cannot be parsed due to invalid characters." +
"Please review it and make sure it is set correctly.", e);
}
if (timeUnit.equals(TimeUnitConfig.HOURS)) {
return expirationTime * 3600;
} else if (timeUnit.equals(TimeUnitConfig.MINUTES)) {
return expirationTime * 60;
} else {
return expirationTime;
}
}
private void reloadAccessToken(OAuth2Profile profile, OAuth2ClientFacade oAuth2Client) {
try {
if (profile.getRefreshToken() != null) {
log.info("The access token has expired, trying to refresh it.");
oAuth2Client.refreshAccessToken(profile);
log.info("The access token has been refreshed successfully.");
} else {
if (profile.hasAutomationJavaScripts()) {
log.info("The access token has expired, trying to retrieve a new one with JavaScript automation.");
oAuth2Client.requestAccessToken(profile);
profile.waitForAccessTokenStatus(OAuth2Profile.AccessTokenStatus.RETRIEVED_FROM_SERVER,
ACCESS_TOKEN_RETRIEVAL_TIMEOUT);
if (profile.getAccessTokenStatus() == OAuth2Profile.AccessTokenStatus.RETRIEVED_FROM_SERVER) {
log.info("A new access token has been retrieved successfully.");
} else {
log.warn("OAuth2 access token retrieval timed out after " + ACCESS_TOKEN_RETRIEVAL_TIMEOUT + " ms");
throw new RuntimeException("OAuth2 access token retrieval timed out after " + ACCESS_TOKEN_RETRIEVAL_TIMEOUT + " ms");
}
} else {
log.warn("No automation JavaScripts added to OAuth2 profile – cannot retrieve new access token");
throw new RuntimeException("No automation JavaScripts added to OAuth2 profile – cannot retrieve new access token");
}
}
} catch (Exception e) {
//Propagate it up so that it is shown as a failure message in test case log
throw new RuntimeException("Unable to refresh expired access token.", e);
}
}
}