package org.owasp.appsensor.analysis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;

import org.owasp.appsensor.Attack;
import org.owasp.appsensor.DetectionPoint;
import org.owasp.appsensor.Response;
import org.owasp.appsensor.AppSensorServer;
import org.owasp.appsensor.logging.Logger;

/**
 * This is the reference {@link Attack} analysis engine, and is an implementation of the Observer pattern. 
 * 
 * It is notified with implementations of the {@link java.util.Observable} interface and is 
 * passed the observed object. In this case, we are only concerned with {@link org.owasp.appsensor.Attack}
 * implementations. 
 * 
 * The implementation performs a simple analysis that checks the created attack against any created {@link Response}s. 
 * It then creates a {@link Response} and adds it to the {@link org.owasp.appsensor.storage.ResponseStore}. 
 * 
 * @see java.util.Observer
 *
 * @author John Melton (jtmelton@gmail.com) http://www.jtmelton.com/
 */
public class ReferenceAttackAnalysisEngine implements AnalysisEngine {

	private static Logger logger = AppSensorServer.getInstance().getLogger().setLoggerClass(ReferenceAttackAnalysisEngine.class);

	/**
	 * This method analyzes {@link org.owasp.appsensor.Attack} objects that are added 
	 * to the system (either via direct addition or generated by the event analysis
	 * engine), generates an appropriate {@link org.owasp.appsensor.Response} object, 
	 * and adds it to the configured {@link org.owasp.appsensor.storage.ResponseStore} 
	 * 
	 * @param observable object that was being obeserved - ignored in this case
	 * @param observedObject object that was added to observable. In this case
	 * 			we are only interested if the object is 
	 * 			an {@link org.owasp.appsensor.Attack} object
	 */
	@Override
	public void update(Observable observable, Object observedObject) {
		if (observedObject instanceof Attack) {
			Attack attack = (Attack)observedObject;
			
			Response response = findAppropriateResponse(attack);
			
			if (response != null) {
				logger.info("Response set for user <" + attack.getUser().getUsername() + "> - storing response action " + response.getAction());
				AppSensorServer.getInstance().getResponseStore().addResponse(response);
			}
		} 
	}
	
	/**
	 * Find/generate response appropriate for specified attack.
	 * 
	 * @param attack attack that is being analyzed
	 * @return response to be executed for given attack
	 */
	protected Response findAppropriateResponse(Attack attack) {
		DetectionPoint triggeringDetectionPoint = attack.getDetectionPoint();
		
		//grab any existing responses
		Collection<Response> existingResponses = 
				AppSensorServer.getInstance().getResponseStore().findResponses(
						attack.getUser(), 
						triggeringDetectionPoint,
						AppSensorServer.getInstance().getConfiguration().getRelatedDetectionSystems(attack.getDetectionSystemId())
						);
		
		Response response = null;
		
		Collection<? extends Response> possibleResponses = findPossibleResponses(triggeringDetectionPoint);
		
		if (existingResponses == null) {
			//no responses yet, just grab first configured response from detection point
			response = possibleResponses.iterator().next();
		} else {
			for (Response configuredResponse : possibleResponses) {
				response = configuredResponse;
				
				if (! isPreviousResponse(response, existingResponses)) {
					//if we find that this response doesn't already exist, use it
					break;
				}
				
				//if we reach here, we will just use the last configured response (repeat last response)
			}
		}
		
		if(response == null) {
			throw new IllegalArgumentException("No appropriate response was configured for this detection point: " + triggeringDetectionPoint.getId());
		}
		
		//set extra fields
		response.setUser(attack.getUser());
		response.setDetectionPoint(triggeringDetectionPoint);
		response.setTimestamp(attack.getTimestamp());
		response.setDetectionSystemId(attack.getDetectionSystemId());
		
		return response;
	}
	
	/**
	 * Lookup configured {@link org.owasp.appsensor.Response} objects for 
	 * specified {@link org.owasp.appsensor.DetectionPoint}
	 * 
	 * @param triggeringDetectionPoint detection point that triggered attack
	 * @return collection of Response objects for given detection point
	 */
	protected Collection<? extends Response> findPossibleResponses(DetectionPoint triggeringDetectionPoint) {
		Collection<? extends Response> possibleResponses = new ArrayList<Response>();
		
		for (DetectionPoint configuredDetectionPoint : AppSensorServer.getInstance().getConfiguration().getDetectionPoints()) {
			if (configuredDetectionPoint.getId().equals(triggeringDetectionPoint.getId())) {
				possibleResponses = configuredDetectionPoint.getResponses();
				break;
			}
		}
		
		return possibleResponses;
	}
	
	/**
	 * Test a given {@link org.owasp.appsensor.Response} to see if it's been executed before.
	 * 
	 * @param response response to test to see if it's been executed before
	 * @param existingResponses set of previously executed responses
	 * @return true if response has been executed before
	 */
	protected boolean isPreviousResponse(Response response, Collection<Response> existingResponses) {
		boolean previousResponse = false;
		
		for (Response existingResponse : existingResponses) {
			if (response.getAction().equals(existingResponse.getAction())) {
				previousResponse = true;
			}
		}
		
		return previousResponse;
	}
}

@@ -119,16 +119,60 @@
 vable;%0A%0A
+import org.owasp.appsensor.AppSensorServer;%0A
 import o
@@ -301,37 +301,36 @@
 p.appsensor.
-AppSensorServ
+logging.Logg
 er;%0Aimport o
@@ -348,30 +348,37 @@
 psensor.
-logging.Logger
+storage.ResponseStore
 ;%0A%0A/**%0A 
@@ -901,36 +901,8 @@
 ink 
-org.owasp.appsensor.storage.
 Resp

update javadoc