/*
* soapUI, copyright (C) 2004-2011 eviware.com
*
* soapUI is free software; you can redistribute it and/or modify it under the
* terms of version 2.1 of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details at gnu.org.
*/
package com.eviware.soapui.security.scan;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.swing.JComponent;
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.SecurityScanRequestResult;
import com.eviware.soapui.security.result.SecurityScanResult;
import com.eviware.soapui.security.result.SecurityResult.ResultStatus;
import com.eviware.soapui.security.support.FailedSecurityMessageExchange;
import com.eviware.soapui.security.support.SecurityTestRunListener;
/**
*
* 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( getAssertableContent() != 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 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
* @param assertion
*/
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
* @param testStep
*/
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;
}
}