/*
* 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.security.scan;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.SecurityScanConfig;
import com.eviware.soapui.config.StrategyTypeConfig;
import com.eviware.soapui.config.TestAssertionConfig;
import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
import com.eviware.soapui.impl.wsdl.WsdlSubmitContext;
import com.eviware.soapui.impl.wsdl.support.assertions.AssertableConfig;
import com.eviware.soapui.impl.wsdl.support.assertions.AssertionsSupport;
import com.eviware.soapui.impl.wsdl.teststeps.TestRequest;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStep;
import com.eviware.soapui.impl.wsdl.teststeps.assertions.TestAssertionRegistry.AssertableType;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.iface.MessageExchange;
import com.eviware.soapui.model.iface.SubmitContext;
import com.eviware.soapui.model.security.SecurityScan;
import com.eviware.soapui.model.testsuite.Assertable;
import com.eviware.soapui.model.testsuite.AssertionError;
import com.eviware.soapui.model.testsuite.AssertionsListener;
import com.eviware.soapui.model.testsuite.ResponseAssertion;
import com.eviware.soapui.model.testsuite.SamplerTestStep;
import com.eviware.soapui.model.testsuite.TestAssertion;
import com.eviware.soapui.model.testsuite.TestStep;
import com.eviware.soapui.security.ExecutionStrategyHolder;
import com.eviware.soapui.security.Securable;
import com.eviware.soapui.security.SecurityTest;
import com.eviware.soapui.security.SecurityTestRunContext;
import com.eviware.soapui.security.SecurityTestRunner;
import com.eviware.soapui.security.SecurityTestRunnerImpl;
import com.eviware.soapui.security.result.SecurityResult.ResultStatus;
import com.eviware.soapui.security.result.SecurityScanRequestResult;
import com.eviware.soapui.security.result.SecurityScanResult;
import com.eviware.soapui.security.support.FailedSecurityMessageExchange;
import com.eviware.soapui.security.support.SecurityTestRunListener;
import javax.swing.JComponent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Implementation that is common for all security scans. Support for security
* workflow.
*
* @author robert
*/
public abstract class AbstractSecurityScan extends AbstractWsdlModelItem<SecurityScanConfig> implements
ResponseAssertion, SecurityScan {
private SecurityScanResult securityScanResult;
private SecurityScanRequestResult securityScanRequestResult;
private TestStep testStep;
protected AssertionsSupport assertionsSupport;
private AssertionStatus currentStatus;
private ExecutionStrategyHolder executionStrategy;
private TestStep originalTestStepClone;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean skipFurtherRunning;
public AbstractSecurityScan(TestStep testStep, SecurityScanConfig config, ModelItem parent, String icon) {
super(config, parent, icon);
if (config == null) {
config = SecurityScanConfig.Factory.newInstance();
setConfig(config);
}
this.testStep = testStep;
if (config.getExecutionStrategy() == null) {
config.addNewExecutionStrategy();
config.getExecutionStrategy().setStrategy(StrategyTypeConfig.ONE_BY_ONE);
config.getExecutionStrategy().setDelay(100);
} else if (config.getExecutionStrategy().getStrategy() == null) {
config.getExecutionStrategy().setStrategy(StrategyTypeConfig.ONE_BY_ONE);
config.getExecutionStrategy().setDelay(100);
}
/*
* if security scan have no strategy, set its value to
* StrategyTypeConfig.NO_STRATEGY.
*/
setExecutionStrategy(new ExecutionStrategyHolder(config.getExecutionStrategy()));
if (config.getCheckedParameters() == null) {
config.addNewCheckedParameters();
}
initAssertions();
setApplyForFailedTestStep(config.getApplyForFailedStep());
if (!config.isSetDisabled()) {
setDisabled(false);
}
}
@Override
public void copyConfig(SecurityScanConfig config) {
super.setConfig(config);
getConfig().setType(config.getType());
getConfig().setName(config.getName());
getConfig().setConfig(config.getConfig());
getConfig().setTestStep(config.getTestStep());
TestAssertionConfig[] assertions = config.getAssertionList().toArray(new TestAssertionConfig[0]);
getConfig().setAssertionArray(assertions);
initAssertions();
getConfig().setExecutionStrategy(config.getExecutionStrategy());
setExecutionStrategy(new ExecutionStrategyHolder(config.getExecutionStrategy()));
}
/*
* (non-Javadoc)
*
* @see
* com.eviware.soapui.security.scan.SecurityScan#updateSecurityConfig(com
* .eviware.soapui.config.SecurityScanConfig)
*/
public void updateSecurityConfig(SecurityScanConfig config) {
setConfig(config);
assertionsSupport.refresh();
if (executionStrategy != null && config.getExecutionStrategy() != null) {
executionStrategy.updateConfig(config.getExecutionStrategy());
}
}
protected void initAssertions() {
assertionsSupport = new AssertionsSupport(this, new AssertableConfig() {
public TestAssertionConfig addNewAssertion() {
return getConfig().addNewAssertion();
}
public List<TestAssertionConfig> getAssertionList() {
return getConfig().getAssertionList();
}
public void removeAssertion(int ix) {
getConfig().removeAssertion(ix);
}
public TestAssertionConfig insertAssertion(TestAssertionConfig source, int ix) {
TestAssertionConfig conf = getConfig().insertNewAssertion(ix);
conf.set(source);
return conf;
}
});
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#run(com.eviware.soapui
* .model.testsuite.TestStep,
* com.eviware.soapui.security.SecurityTestRunContext,
* com.eviware.soapui.security.SecurityTestRunner)
*/
public SecurityScanResult run(TestStep testStep, SecurityTestRunContext context,
SecurityTestRunner securityTestRunner) {
securityScanResult = new SecurityScanResult(this);
SecurityTestRunListener[] securityTestListeners = ((SecurityTest) getParent()).getSecurityTestRunListeners();
PropertyChangeNotifier notifier = new PropertyChangeNotifier();
boolean noMutations = true;
while (hasNext(testStep, context)) {
noMutations = false;
if (((SecurityTestRunnerImpl) securityTestRunner).isCanceled()) {
securityScanResult.setStatus(ResultStatus.CANCELED);
clear();
return securityScanResult;
}
securityScanRequestResult = new SecurityScanRequestResult(this);
securityScanRequestResult.startTimer();
originalTestStepClone = ((SecurityTestRunnerImpl) securityTestRunner)
.cloneForSecurityScan((WsdlTestStep) this.testStep);
execute(securityTestRunner, originalTestStepClone, context);
notifier.notifyChange();
securityScanRequestResult.stopTimer();
assertResponse(getSecurityScanRequestResult().getMessageExchange(), context);
// add to summary result
securityScanResult.addSecurityRequestResult(getSecurityScanRequestResult());
for (int i = 0; i < securityTestListeners.length; i++) {
if (Arrays.asList(((SecurityTest) getParent()).getSecurityTestRunListeners()).contains(
securityTestListeners[i])) {
securityTestListeners[i].afterSecurityScanRequest((SecurityTestRunnerImpl) securityTestRunner,
context, securityScanRequestResult);
}
}
try {
Thread.sleep(getExecutionStrategy().getDelay());
} catch (InterruptedException e) {
SoapUI.logError(e, "Security Scan Request Delay Interrupted!");
}
}
if (noMutations) {
securityScanResult.setStatus(ResultStatus.SKIPPED);
}
return securityScanResult;
}
protected void clear() {
}
/**
* should be implemented in every particular scan it executes one request,
* modified by securityScan if necessary and internally adds messages for
* logging to SecurityScanRequestResult
*/
abstract protected void execute(SecurityTestRunner runner, TestStep testStep, SecurityTestRunContext context);
/**
* checks if specific SecurityScan still has modifications left
*
* @param testStep2
* @param context
*/
abstract protected boolean hasNext(TestStep testStep2, SecurityTestRunContext context);
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#isConfigurable()
*/
public boolean isConfigurable() {
return true;
}
/**
* Overide if SecurityScan have Optional component
*/
@Override
public JComponent getComponent() {
return null;
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getType()
*/
public abstract String getType();
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getTestStep()
*/
public TestStep getTestStep() {
return testStep;
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#setTestStep(com.eviware
* .soapui.model.testsuite.TestStep)
*/
public void setTestStep(TestStep step) {
testStep = step;
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#isDisabled()
*/
public boolean isDisabled() {
return getConfig().getDisabled();
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#setDisabled(boolean)
*/
public void setDisabled(boolean disabled) {
boolean oldValue = isDisabled();
getConfig().setDisabled(disabled);
pcs.firePropertyChange("disabled", oldValue, disabled);
}
public static boolean isSecurable(TestStep testStep) {
if (testStep != null && testStep instanceof Securable) {
return true;
} else {
return false;
}
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getExecutionStrategy()
*/
public ExecutionStrategyHolder getExecutionStrategy() {
return this.executionStrategy;
}
/*
* (non-Javadoc)
*
* @see
* com.eviware.soapui.security.scan.SecurityScan#setExecutionStrategy(com
* .eviware.soapui.security.ExecutionStrategyHolder)
*/
public void setExecutionStrategy(ExecutionStrategyHolder executionStrategy) {
ExecutionStrategyHolder oldValue = getExecutionStrategy();
this.executionStrategy = executionStrategy;
pcs.firePropertyChange("executionStrategy", oldValue, executionStrategy);
}
protected TestRequest getOriginalResult(SecurityTestRunnerImpl securityRunner, TestStep testStep) {
testStep.run(securityRunner, securityRunner.getRunContext());
return getRequest(testStep);
}
protected TestRequest getRequest(TestStep testStep) {
if (testStep instanceof SamplerTestStep) {
return ((SamplerTestStep) testStep).getTestRequest();
}
return null;
}
private class PropertyChangeNotifier {
private ResultStatus oldStatus;
public PropertyChangeNotifier() {
oldStatus = getSecurityStatus();
}
public void notifyChange() {
ResultStatus newStatus = getSecurityStatus();
if (oldStatus != newStatus) {
notifyPropertyChanged(STATUS_PROPERTY, oldStatus, newStatus);
}
oldStatus = newStatus;
}
}
@Override
public TestAssertion addAssertion(String label) {
PropertyChangeNotifier notifier = new PropertyChangeNotifier();
try {
WsdlMessageAssertion assertion = assertionsSupport.addWsdlAssertion(label);
if (assertion == null) {
return null;
}
if (getAssertableContentAsXml() != null) {
assertRequests(assertion);
assertResponses(assertion);
notifier.notifyChange();
}
return assertion;
} catch (Exception e) {
SoapUI.logError(e);
return null;
}
}
/**
* @param assertion run all responses against this assertion
*/
private void assertResponses(WsdlMessageAssertion assertion) {
if (securityScanResult != null) {
for (SecurityScanRequestResult result : securityScanResult.getSecurityRequestResultList()) {
if (result.getMessageExchange() == null) {
return;
}
assertion.assertResponse(result.getMessageExchange(), new WsdlSubmitContext(testStep));
}
}
}
/**
* @param assertion run all request against this assertion
*/
private void assertRequests(WsdlMessageAssertion assertion) {
if (securityScanResult != null) {
for (SecurityScanRequestResult result : securityScanResult.getSecurityRequestResultList()) {
if (result.getMessageExchange() == null) {
return;
}
assertion.assertRequest(result.getMessageExchange(), new WsdlSubmitContext(testStep));
}
}
}
@Override
public void removeAssertion(TestAssertion assertion) {
PropertyChangeNotifier notifier = new PropertyChangeNotifier();
try {
assertionsSupport.removeAssertion((WsdlMessageAssertion) assertion);
} finally {
((WsdlMessageAssertion) assertion).release();
notifier.notifyChange();
}
}
@Override
public TestAssertion moveAssertion(int ix, int offset) {
WsdlMessageAssertion assertion = getAssertionAt(ix);
PropertyChangeNotifier notifier = new PropertyChangeNotifier();
try {
return assertionsSupport.moveAssertion(ix, offset);
} finally {
((WsdlMessageAssertion) assertion).release();
notifier.notifyChange();
}
}
@Override
public WsdlMessageAssertion getAssertionAt(int c) {
return assertionsSupport.getAssertionAt(c);
}
@Override
public void addAssertionsListener(AssertionsListener listener) {
assertionsSupport.addAssertionsListener(listener);
}
@Override
public void removeAssertionsListener(AssertionsListener listener) {
assertionsSupport.removeAssertionsListener(listener);
}
@Override
public int getAssertionCount() {
return assertionsSupport.getAssertionCount();
}
@Override
public AssertionStatus getAssertionStatus() {
int cnt = getAssertionCount();
if (cnt == 0) {
return currentStatus;
}
if (securityScanResult != null && securityScanResult.getStatus() == ResultStatus.OK) {
currentStatus = AssertionStatus.VALID;
} else {
currentStatus = AssertionStatus.FAILED;
}
return currentStatus;
}
public ResultStatus getSecurityStatus() {
return securityScanResult != null ? securityScanResult.getStatus() : ResultStatus.UNKNOWN;
}
@Override
public String getAssertableContentAsXml() {
if (testStep instanceof Assertable) {
return ((Assertable) testStep).getAssertableContentAsXml();
}
return null;
}
@Override
public String getAssertableContent() {
if (testStep instanceof Assertable) {
return ((Assertable) testStep).getAssertableContent();
}
return null;
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.model.testsuite.Assertable#getAssertableType()
*
* Decided to go with assertions on request and response so we can implement
* "men in the middle" attacks using monitor.
*/
@Override
public AssertableType getAssertableType() {
return AssertableType.BOTH;
}
@Override
public TestAssertion getAssertionByName(String name) {
return assertionsSupport.getAssertionByName(name);
}
@Override
public List<TestAssertion> getAssertionList() {
return new ArrayList<TestAssertion>(assertionsSupport.getAssertionList());
}
@Override
public Map<String, TestAssertion> getAssertions() {
return assertionsSupport.getAssertions();
}
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getAssertionsSupport()
*/
public AssertionsSupport getAssertionsSupport() {
return assertionsSupport;
}
@Override
public TestAssertion cloneAssertion(TestAssertion source, String name) {
return assertionsSupport.cloneAssertion(source, name);
}
@Override
public String getDefaultAssertableContent() {
if (testStep instanceof Assertable) {
return ((Assertable) testStep).getDefaultAssertableContent();
}
return null;
}
@Override
public Interface getInterface() {
if (testStep instanceof WsdlTestRequestStep) {
return ((WsdlTestRequestStep) testStep).getInterface();
}
return null;
}
@Override
public ModelItem getModelItem() {
return this;
}
public AssertionStatus assertResponse(MessageExchange messageExchange, SubmitContext context) {
AssertionStatus finalResult = null;
try {
PropertyChangeNotifier notifier = new PropertyChangeNotifier();
if (messageExchange != null) {
context.setProperty(SECURITY_SCAN_REQUEST_RESULT, getSecurityScanRequestResult());
for (WsdlMessageAssertion assertion : assertionsSupport.getAssertionList()) {
AssertionStatus currentResult = assertion.assertResponse(messageExchange, context);
updateMessages(currentResult, assertion);
if (finalResult == null || finalResult != AssertionStatus.FAILED) {
finalResult = currentResult;
}
}
setStatus(finalResult);
notifier.notifyChange();
}
} catch (Exception e) {
e.printStackTrace();
}
return finalResult;
}
/**
* Sets SecurityScanStatus based on the status of all assertions added
*
* @param result
*/
private void setStatus(AssertionStatus result) {
if (result == AssertionStatus.FAILED) {
getSecurityScanRequestResult().setStatus(ResultStatus.FAILED);
} else if (result == AssertionStatus.VALID) {
getSecurityScanRequestResult().setStatus(ResultStatus.OK);
} else if (result == AssertionStatus.UNKNOWN) {
getSecurityScanRequestResult().setStatus(ResultStatus.UNKNOWN);
}
}
private void updateMessages(AssertionStatus result, WsdlMessageAssertion assertion) {
if (result == AssertionStatus.FAILED) {
for (AssertionError error : assertion.getErrors()) {
getSecurityScanRequestResult().addMessage(error.getMessage());
}
}
}
// name used in configuration panel
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getConfigName()
*/
public abstract String getConfigName();
// description usd in configuration panel
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getConfigDescription()
*/
public abstract String getConfigDescription();
// help url used for configuration panel ( help for this scan )
/*
* (non-Javadoc)
*
* @see com.eviware.soapui.security.scan.SecurityScan#getHelpURL()
*/
public abstract String getHelpURL();
protected void setSecurityScanRequestResult(SecurityScanRequestResult securityScanRequestResult) {
this.securityScanRequestResult = securityScanRequestResult;
}
protected SecurityScanRequestResult getSecurityScanRequestResult() {
return securityScanRequestResult;
}
/**
* Overide if SecurityScan needs advanced settings
*/
@Override
public JComponent getAdvancedSettingsPanel() {
return null;
}
@Override
public SecurityScanResult getSecurityScanResult() {
return securityScanResult;
}
/**
* @param message
*/
protected void reportSecurityScanException(String message) {
getSecurityScanRequestResult().setMessageExchange(new FailedSecurityMessageExchange());
getSecurityScanRequestResult().setStatus(ResultStatus.FAILED);
getSecurityScanRequestResult().addMessage(message);
}
@Override
public void addWsdlAssertion(String assertionLabel) {
assertionsSupport.addWsdlAssertion(assertionLabel);
}
@Override
public boolean isApplyForFailedStep() {
return getConfig().getApplyForFailedStep();
}
@Override
public void setApplyForFailedTestStep(boolean apply) {
getConfig().setApplyForFailedStep(apply);
}
@Override
public boolean isRunOnlyOnce() {
return getConfig().getRunOnlyOnce();
}
@Override
public void setRunOnlyOnce(boolean runOnlyOnce) {
getConfig().setRunOnlyOnce(runOnlyOnce);
}
public void release() {
if (assertionsSupport != null) {
assertionsSupport.release();
}
if (securityScanResult != null) {
securityScanResult.release();
}
if (securityScanRequestResult != null) {
securityScanRequestResult.release();
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
@Override
public boolean isSkipFurtherRunning() {
return skipFurtherRunning;
}
@Override
public void setSkipFurtherRunning(boolean skipFurtherRunning) {
this.skipFurtherRunning = skipFurtherRunning;
}
}