/* * 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; import com.eviware.soapui.config.AccessTokenPositionConfig; import com.eviware.soapui.config.AccessTokenStatusConfig; import com.eviware.soapui.config.OAuth2FlowConfig; import com.eviware.soapui.config.OAuth2ProfileConfig; import com.eviware.soapui.config.RefreshAccessTokenMethodConfig; import com.eviware.soapui.config.StringListConfig; import com.eviware.soapui.config.TimeUnitConfig; import com.eviware.soapui.model.propertyexpansion.PropertyExpansion; import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContainer; import com.eviware.soapui.model.propertyexpansion.PropertyExpansionsResult; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import org.apache.commons.lang.StringUtils; import javax.annotation.Nonnull; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static com.eviware.soapui.impl.rest.OAuth2Profile.RefreshAccessTokenMethods.AUTOMATIC; /** * Encapsulates values associated with an Oauth2 flow. Mostly they will be input by users, but the "accessToken" and * "status" properties will be modified during the OAuth2 interactions. */ public class OAuth2Profile implements PropertyExpansionContainer { public static final String CLIENT_ID_PROPERTY = "clientID"; public static final String CLIENT_SECRET_PROPERTY = "clientSecret"; public static final String AUTHORIZATION_URI_PROPERTY = "authorizationURI"; public static final String ACCESS_TOKEN_URI_PROPERTY = "accessTokenURI"; public static final String REDIRECT_URI_PROPERTY = "redirectURI"; public static final String ACCESS_TOKEN_PROPERTY = "accessToken"; public static final String REFRESH_TOKEN_PROPERTY = "refreshToken"; public static final String SCOPE_PROPERTY = "scope"; public static final String ACCESS_TOKEN_STATUS_PROPERTY = "accessTokenStatus"; public static final String ACCESS_TOKEN_POSITION_PROPERTY = "accessTokenPosition"; public static final String ACCESS_TOKEN_EXPIRATION_TIME = "accessTokenExpirationTime"; public static final String ACCESS_TOKEN_ISSUED_TIME = "accessTokenIssuedTime"; public static final String MANUAL_ACCESS_TOKEN_EXPIRATION_TIME = "manualAccessTokenExpirationTime"; public static final String USE_MANUAL_ACCESS_TOKEN_EXPIRATION_TIME = "useManualAccessTokenExpirationTime"; public static final String REFRESH_ACCESS_TOKEN_METHOD_PROPERTY = "refreshAccessTokenMethod"; public static final String OAUTH2_FLOW_PROPERTY = "oAuth2Flow"; public static final String JAVA_SCRIPTS_PROPERTY = "javaScripts"; public static final String MANUAL_ACCESS_TOKEN_EXPIRATION_TIME_UNIT_PROPERTY = "manualAccessTokenExpirationTimeUnit"; public static final String RESOURCE_OWNER_LOGIN_PROPERTY = "resourceOwnerName"; public static final String RESOURCE_OWNER_PASSWORD_PROPERTY = "resourceOwnerPassword"; public void waitForAccessTokenStatus(AccessTokenStatus accessTokenStatus, int timeout) { int timeLeft = timeout; while ((getAccessTokenStatus() != accessTokenStatus) && timeLeft > 0) { long startTime = System.currentTimeMillis(); try { synchronized (this) { wait(timeLeft); } } catch (InterruptedException ignore) { } timeLeft -= (System.currentTimeMillis() - startTime); } } public enum AccessTokenStatus { UNKNOWN("Unknown"), ENTERED_MANUALLY("Entered Manually"), WAITING_FOR_AUTHORIZATION("Waiting for Authorization"), RECEIVED_AUTHORIZATION_CODE("Received authorization code"), RETRIEVED_FROM_SERVER("Retrieved from server"), RETRIEVAL_CANCELED("Retrieval canceled"), EXPIRED("Expired"); private String description; AccessTokenStatus(String description) { this.description = description; } @Override public String toString() { return description; } } public enum AccessTokenPosition { QUERY("Query"), HEADER("Header"), BODY("Body"); private String description; AccessTokenPosition(String description) { this.description = description; } @Override public String toString() { return description; } } public enum OAuth2Flow { AUTHORIZATION_CODE_GRANT("Authorization Code Grant"), IMPLICIT_GRANT("Implicit Grant"), RESOURCE_OWNER_PASSWORD_CREDENTIALS("Resource Owner Password Credentials Grant"), CLIENT_CREDENTIALS_GRANT("Client Credentials Grant"); private String description; OAuth2Flow(String description) { this.description = description; } @Override public String toString() { return description; } } public enum RefreshAccessTokenMethods { AUTOMATIC("Automatic"), MANUAL("Manual"); private final String description; RefreshAccessTokenMethods(String description) { this.description = description; } @Override public String toString() { return description; } } private final OAuth2ProfileContainer oAuth2ProfileContainer; private final OAuth2ProfileConfig configuration; private final PropertyChangeSupport pcs; public OAuth2Profile(OAuth2ProfileContainer oAuth2ProfileContainer, OAuth2ProfileConfig configuration) { this.oAuth2ProfileContainer = oAuth2ProfileContainer; this.configuration = configuration; pcs = new PropertyChangeSupport(this); setDefaultAccessTokenPosition(); setDefaultRefreshMethod(); setDefaultAccessTokenStatus(); } public String getName() { //TODO: this is only for backward compatibility where we had only one profile without name, should be removed in 5.1 if (StringUtils.isEmpty(configuration.getName())) { configuration.setName("OAuth 2 - Profile 1"); } return configuration.getName(); } public void setName(String newName) { configuration.setName(newName); } public boolean hasAutomationJavaScripts() { List<String> javaScripts = getAutomationJavaScripts(); return javaScripts != null && !javaScripts.isEmpty(); } public void applyRetrievedAccessToken(String accessToken) { // Ignore return value in this case: even if it is not a change, it is important to know that a token has been // retrieved from the server doSetAccessToken(accessToken); setAccessTokenStatus(AccessTokenStatus.RETRIEVED_FROM_SERVER); } public String getAccessToken() { return configuration.getAccessToken(); } /** * NOTE: This setter should only be used from the GUI, because it also sets the property "accessTokenStatus" to * ENTERED_MANUALLY * * @param accessToken the access token supplied by the user */ public void setAccessToken(String accessToken) { if (doSetAccessToken(accessToken)) { setAccessTokenStatus(AccessTokenStatus.ENTERED_MANUALLY); } } public void setOAuth2Flow(OAuth2Flow oauth2Flow) { OAuth2Flow existingFlow = getOAuth2Flow(); if (!oauth2Flow.equals(existingFlow)) { configuration.setOAuth2Flow(OAuth2FlowConfig.Enum.forString(oauth2Flow.name())); pcs.firePropertyChange(OAUTH2_FLOW_PROPERTY, existingFlow, oauth2Flow); } } public OAuth2Flow getOAuth2Flow() { if (configuration.getOAuth2Flow() == null) { configuration.setOAuth2Flow(OAuth2FlowConfig.AUTHORIZATION_CODE_GRANT); } return OAuth2Flow.valueOf(configuration.getOAuth2Flow().toString()); } public String getRefreshToken() { return configuration.getRefreshToken(); } public void setRefreshToken(String refreshToken) { String oldValue = configuration.getRefreshToken(); if (!StringUtils.equals(oldValue, refreshToken)) { configuration.setRefreshToken(refreshToken); pcs.firePropertyChange(REFRESH_TOKEN_PROPERTY, oldValue, refreshToken); } } private boolean doSetAccessToken(String accessToken) { String oldValue = configuration.getAccessToken(); String newValue = accessToken == null ? null : accessToken.trim(); if (!StringUtils.equals(oldValue, newValue)) { configuration.setAccessToken(newValue); pcs.firePropertyChange(ACCESS_TOKEN_PROPERTY, oldValue, newValue); return true; } return false; } public String getAuthorizationURI() { return configuration.getAuthorizationURI(); } public void setAuthorizationURI(String authorizationURI) { String oldValue = configuration.getAuthorizationURI(); String newValue = nullSafeTrim(authorizationURI); if (!StringUtils.equals(oldValue, newValue)) { configuration.setAuthorizationURI(newValue); pcs.firePropertyChange(AUTHORIZATION_URI_PROPERTY, oldValue, newValue); } } private String nullSafeTrim(String inputString) { return inputString == null ? null : inputString.trim(); } public String getClientID() { return configuration.getClientID(); } public void setClientID(String clientID) { String oldValue = configuration.getClientID(); String newValue = nullSafeTrim(clientID); if (!StringUtils.equals(oldValue, newValue)) { configuration.setClientID(newValue); pcs.firePropertyChange(CLIENT_ID_PROPERTY, oldValue, newValue); } } public String getClientSecret() { return configuration.getClientSecret(); } public void setClientSecret(String clientSecret) { String oldValue = configuration.getClientSecret(); if (!StringUtils.equals(oldValue, clientSecret)) { configuration.setClientSecret(clientSecret); pcs.firePropertyChange(CLIENT_SECRET_PROPERTY, oldValue, clientSecret); } } public String getResourceOwnerName() { return configuration.getResourceOwnerName(); } public void setResourceOwnerName(String resourceOwnerName) { String oldValue = configuration.getResourceOwnerName(); if (!StringUtils.equals(oldValue, resourceOwnerName)) { configuration.setResourceOwnerName(resourceOwnerName); pcs.firePropertyChange(RESOURCE_OWNER_LOGIN_PROPERTY, oldValue, resourceOwnerName); } } public String getResourceOwnerPassword() { return configuration.getResourceOwnerPassword(); } public void setResourceOwnerPassword(String resourceOwnerPassword) { String oldValue = configuration.getResourceOwnerPassword(); if (!StringUtils.equals(oldValue, resourceOwnerPassword)) { configuration.setResourceOwnerPassword(resourceOwnerPassword); pcs.firePropertyChange(RESOURCE_OWNER_PASSWORD_PROPERTY, oldValue, resourceOwnerPassword); } } public String getRedirectURI() { return configuration.getRedirectURI(); } public void setRedirectURI(String redirectURI) { String oldValue = configuration.getRedirectURI(); if (!StringUtils.equals(oldValue, redirectURI)) { configuration.setRedirectURI(redirectURI); pcs.firePropertyChange(REDIRECT_URI_PROPERTY, oldValue, redirectURI); } } public String getScope() { return configuration.getScope(); } public void setScope(String scope) { String oldValue = configuration.getScope(); if (!StringUtils.equals(oldValue, scope)) { configuration.setScope(scope); pcs.firePropertyChange(SCOPE_PROPERTY, oldValue, scope); } } public OAuth2ProfileConfig getConfiguration() { return configuration; } public String getAccessTokenURI() { return configuration.getAccessTokenURI(); } public void setAccessTokenURI(String accessTokenURI) { String oldValue = configuration.getAccessTokenURI(); String newValue = nullSafeTrim(accessTokenURI); if (!StringUtils.equals(oldValue, newValue)) { configuration.setAccessTokenURI(newValue); pcs.firePropertyChange(ACCESS_TOKEN_URI_PROPERTY, oldValue, newValue); } } public AccessTokenStatus getAccessTokenStatus() { return getSavedAccessTokenStatusEnum(configuration.getAccessTokenStatus()); } public void setAccessTokenStatus(AccessTokenStatus newStatus) { AccessTokenStatus oldStatus = getSavedAccessTokenStatusEnum(configuration.getAccessTokenStatus()); if (newStatus == oldStatus) { return; } saveAccessTokenStatusEnum(newStatus, configuration); if (isAStartingStatus(newStatus)) { setAccessTokenStartingStatus(newStatus); } pcs.firePropertyChange(ACCESS_TOKEN_STATUS_PROPERTY, oldStatus, newStatus); } public AccessTokenStatus getAccessTokenStartingStatus() { return getSavedAccessTokenStartingStatusEnum(configuration.getAccessTokenStartingStatus()); } public void resetAccessTokenStatusToStartingStatus() { setAccessTokenStatus(getAccessTokenStartingStatus()); } public AccessTokenPosition getAccessTokenPosition() { return getSavedAccessTokenPositionEnum(configuration.getAccessTokenPosition()); } public void setAccessTokenPosition(@Nonnull AccessTokenPosition newAccessTokenPosition) { Preconditions.checkNotNull(newAccessTokenPosition); AccessTokenPosition oldAccessTokenPosition = getSavedAccessTokenPositionEnum(configuration.getAccessTokenPosition()); saveAccessTokenPositionEnum(newAccessTokenPosition, configuration); pcs.firePropertyChange(ACCESS_TOKEN_POSITION_PROPERTY, oldAccessTokenPosition, newAccessTokenPosition); } public RefreshAccessTokenMethods getRefreshAccessTokenMethod() { return getSavedRefreshAccessTokenMethodsEnum(configuration.getRefreshAccessTokenMethod()); } public void setRefreshAccessTokenMethod(@Nonnull RefreshAccessTokenMethods newRefreshAccessTokenMethod) { Preconditions.checkNotNull(newRefreshAccessTokenMethod); RefreshAccessTokenMethods oldRefreshTokenMethod = getSavedRefreshAccessTokenMethodsEnum(configuration.getRefreshAccessTokenMethod()); saveRefreshTokenMethodsEnum(newRefreshAccessTokenMethod, configuration); pcs.firePropertyChange(REFRESH_ACCESS_TOKEN_METHOD_PROPERTY, oldRefreshTokenMethod, newRefreshAccessTokenMethod); } public long getAccessTokenExpirationTime() { return configuration.getAccessTokenExpirationTime(); } public void setAccessTokenExpirationTime(long newExpirationTime) { long oldExpirationTime = configuration.getAccessTokenExpirationTime(); if (oldExpirationTime != newExpirationTime) { configuration.setAccessTokenExpirationTime(newExpirationTime); pcs.firePropertyChange(ACCESS_TOKEN_EXPIRATION_TIME, oldExpirationTime, newExpirationTime); } } public long getAccessTokenIssuedTime() { return configuration.getAccessTokenIssuedTime(); } public void setAccessTokenIssuedTime(long newIssuedTime) { long oldIssuedTime = configuration.getAccessTokenIssuedTime(); if (oldIssuedTime != newIssuedTime) { configuration.setAccessTokenIssuedTime(newIssuedTime); pcs.firePropertyChange(ACCESS_TOKEN_ISSUED_TIME, oldIssuedTime, newIssuedTime); } } public String getManualAccessTokenExpirationTime() { return configuration.getManualAccessTokenExpirationTime(); } public void setManualAccessTokenExpirationTime(@Nonnull String newExpirationTime) { String oldExpirationTime = configuration.getManualAccessTokenExpirationTime(); if (!Objects.equal(oldExpirationTime, newExpirationTime)) { configuration.setManualAccessTokenExpirationTime(newExpirationTime); pcs.firePropertyChange(MANUAL_ACCESS_TOKEN_EXPIRATION_TIME, oldExpirationTime, newExpirationTime); } } public boolean useManualAccessTokenExpirationTime() { return configuration.getUseManualAccessTokenExpirationTime(); } public void setUseManualAccessTokenExpirationTime(boolean useManual) { boolean oldValue = configuration.getUseManualAccessTokenExpirationTime(); if (oldValue != useManual) { configuration.setUseManualAccessTokenExpirationTime(useManual); pcs.firePropertyChange(USE_MANUAL_ACCESS_TOKEN_EXPIRATION_TIME, oldValue, useManual); } } public TimeUnitConfig.Enum getManualAccessTokenExpirationTimeUnit() { if (configuration.getManualAccessTokenExpirationTimeUnit() == null) { configuration.setManualAccessTokenExpirationTimeUnit(TimeUnitConfig.SECONDS); } return configuration.getManualAccessTokenExpirationTimeUnit(); } public void setManualAccessTokenExpirationTimeUnit(TimeUnitConfig.Enum newValue) { TimeUnitConfig.Enum oldValue = getManualAccessTokenExpirationTimeUnit(); if (!oldValue.equals(newValue)) { configuration.setManualAccessTokenExpirationTimeUnit(newValue); pcs.firePropertyChange(MANUAL_ACCESS_TOKEN_EXPIRATION_TIME_UNIT_PROPERTY, oldValue.toString(), newValue.toString()); } } public boolean shouldReloadAccessTokenAutomatically() { return getRefreshAccessTokenMethod().equals(AUTOMATIC) && (!StringUtils.isEmpty(getRefreshToken()) || hasAutomationJavaScripts()); } public OAuth2ProfileContainer getContainer() { return oAuth2ProfileContainer; } @Override public PropertyExpansion[] getPropertyExpansions() { PropertyExpansionsResult result = new PropertyExpansionsResult(oAuth2ProfileContainer.getProject(), this); result.extractAndAddAll(CLIENT_ID_PROPERTY); result.extractAndAddAll(CLIENT_SECRET_PROPERTY); result.extractAndAddAll(AUTHORIZATION_URI_PROPERTY); result.extractAndAddAll(ACCESS_TOKEN_URI_PROPERTY); result.extractAndAddAll(REDIRECT_URI_PROPERTY); result.extractAndAddAll(ACCESS_TOKEN_PROPERTY); result.extractAndAddAll(SCOPE_PROPERTY); result.extractAndAddAll(MANUAL_ACCESS_TOKEN_EXPIRATION_TIME); result.extractAndAddAll(RESOURCE_OWNER_LOGIN_PROPERTY); result.extractAndAddAll(RESOURCE_OWNER_PASSWORD_PROPERTY); return result.toArray(); } public List<String> getAutomationJavaScripts() { StringListConfig configurationEntry = configuration.getJavaScripts(); return configurationEntry == null ? Collections.<String>emptyList() : new ArrayList<String>( configurationEntry.getEntryList()); } public void setAutomationJavaScripts(List<String> javaScripts) { List<String> oldScripts = getAutomationJavaScripts(); String[] scriptArray = javaScripts.toArray(new String[javaScripts.size()]); StringListConfig javaScriptsConfiguration = configuration.getJavaScripts(); if (javaScriptsConfiguration == null) { javaScriptsConfiguration = StringListConfig.Factory.newInstance(); } javaScriptsConfiguration.setEntryArray(scriptArray); configuration.setJavaScripts(javaScriptsConfiguration); pcs.firePropertyChange(JAVA_SCRIPTS_PROPERTY, oldScripts, javaScripts); } public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.addPropertyChangeListener(propertyName, listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.removePropertyChangeListener(propertyName, listener); } private void setAccessTokenStartingStatus(@Nonnull AccessTokenStatus startingStatus) { Preconditions.checkNotNull(startingStatus); saveAccessTokenStartingStatusEnum(startingStatus, configuration); } private boolean isAStartingStatus(AccessTokenStatus newStatus) { return newStatus == AccessTokenStatus.ENTERED_MANUALLY || newStatus == AccessTokenStatus.RETRIEVED_FROM_SERVER || newStatus == AccessTokenStatus.EXPIRED; } private void setDefaultAccessTokenPosition() { if (getAccessTokenPosition() == null) { setAccessTokenPosition(AccessTokenPosition.HEADER); } } private void setDefaultRefreshMethod() { if (getRefreshAccessTokenMethod() == null) { setRefreshAccessTokenMethod(RefreshAccessTokenMethods.AUTOMATIC); } } private void setDefaultAccessTokenStatus() { setAccessTokenStatus(AccessTokenStatus.UNKNOWN); } private AccessTokenStatus getSavedAccessTokenStartingStatusEnum(AccessTokenStatusConfig.Enum persistedEnum) { return getSavedAccessTokenStatusEnum(persistedEnum); } private AccessTokenStatus getSavedAccessTokenStatusEnum(AccessTokenStatusConfig.Enum persistedEnum) { if (persistedEnum == null) { return AccessTokenStatus.UNKNOWN; } else { return AccessTokenStatus.valueOf(persistedEnum.toString()); } } private AccessTokenPosition getSavedAccessTokenPositionEnum(AccessTokenPositionConfig.Enum persistedEnum) { if (persistedEnum == null) { return null; } else { return AccessTokenPosition.valueOf(persistedEnum.toString()); } } private RefreshAccessTokenMethods getSavedRefreshAccessTokenMethodsEnum(RefreshAccessTokenMethodConfig.Enum persistedEnum) { if (persistedEnum == null) { return null; } else { return RefreshAccessTokenMethods.valueOf(persistedEnum.toString()); } } private void saveAccessTokenStatusEnum(AccessTokenStatus enumToBePersisted, OAuth2ProfileConfig configuration) { configuration.setAccessTokenStatus(AccessTokenStatusConfig.Enum.forString(enumToBePersisted.name())); } private void saveAccessTokenStartingStatusEnum(AccessTokenStatus enumToBePersisted, OAuth2ProfileConfig configuration) { configuration.setAccessTokenStartingStatus(AccessTokenStatusConfig.Enum.forString(enumToBePersisted.name())); } private void saveAccessTokenPositionEnum(AccessTokenPosition enumToBePersisted, OAuth2ProfileConfig configuration) { configuration.setAccessTokenPosition(AccessTokenPositionConfig.Enum.forString(enumToBePersisted.name())); } private void saveRefreshTokenMethodsEnum(RefreshAccessTokenMethods enumToBePersisted, OAuth2ProfileConfig configuration) { configuration.setRefreshAccessTokenMethod(RefreshAccessTokenMethodConfig.Enum.forString(enumToBePersisted.name())); } }