package com.threatconnect.sdk.parser.service.save;
import com.threatconnect.sdk.config.Configuration;
import com.threatconnect.sdk.conn.Connection;
import com.threatconnect.sdk.parser.model.Address;
import com.threatconnect.sdk.parser.model.Adversary;
import com.threatconnect.sdk.parser.model.CustomIndicator;
import com.threatconnect.sdk.parser.model.Document;
import com.threatconnect.sdk.parser.model.Email;
import com.threatconnect.sdk.parser.model.EmailAddress;
import com.threatconnect.sdk.parser.model.File;
import com.threatconnect.sdk.parser.model.Group;
import com.threatconnect.sdk.parser.model.Host;
import com.threatconnect.sdk.parser.model.Incident;
import com.threatconnect.sdk.parser.model.Indicator;
import com.threatconnect.sdk.parser.model.Item;
import com.threatconnect.sdk.parser.model.Signature;
import com.threatconnect.sdk.parser.model.Threat;
import com.threatconnect.sdk.parser.model.Url;
import com.threatconnect.sdk.parser.service.writer.AddressWriter;
import com.threatconnect.sdk.parser.service.writer.AdversaryWriter;
import com.threatconnect.sdk.parser.service.writer.CustomIndicatorWriter;
import com.threatconnect.sdk.parser.service.writer.DocumentWriter;
import com.threatconnect.sdk.parser.service.writer.EmailAddressWriter;
import com.threatconnect.sdk.parser.service.writer.EmailWriter;
import com.threatconnect.sdk.parser.service.writer.FileWriter;
import com.threatconnect.sdk.parser.service.writer.GroupWriter;
import com.threatconnect.sdk.parser.service.writer.HostWriter;
import com.threatconnect.sdk.parser.service.writer.IncidentWriter;
import com.threatconnect.sdk.parser.service.writer.IndicatorWriter;
import com.threatconnect.sdk.parser.service.writer.SignatureWriter;
import com.threatconnect.sdk.parser.service.writer.ThreatWriter;
import com.threatconnect.sdk.parser.service.writer.UrlWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Collection;
/**
* Responsible for saving the model to the server using the threatconnect sdk
*
* @author Greg Marut
*/
public class ApiSaveService implements SaveService
{
private static final Logger logger = LoggerFactory.getLogger(ApiSaveService.class);
private final Configuration configuration;
private final String ownerName;
public ApiSaveService(final Configuration configuration, final String ownerName)
{
this.configuration = configuration;
this.ownerName = ownerName;
}
/**
* Saves all of the items to the server using the APIs
*
* @param items
* @throws IOException
* Signals that an I/O exception of some sort has occurred. This
* class is the general class of exceptions produced by failed or
* interrupted I/O operations.
*/
@Override
public SaveResults saveItems(final Collection<? extends Item> items) throws IOException
{
// create a new connection object from the configuration
Connection connection = new Connection(configuration);
return saveItems(items, connection);
}
/**
* Saves all of the items to the server using the APIs
*
* @param items
* @param connection
* @throws IOException
* Signals that an I/O exception of some sort has occurred. This
* class is the general class of exceptions produced by failed or
* interrupted I/O operations.
*/
protected SaveResults saveItems(final Collection<? extends Item> items, final Connection connection)
throws IOException
{
// create a new save result to return
SaveResults saveResults = new SaveResults();
// for each of the items
for (Item item : items)
{
saveItem(item, ownerName, connection, saveResults);
}
return saveResults;
}
/**
* Saves a single item
*
* @param item
* @param ownerName
* @param connection
* @param saveResults
* @throws IOException
* @throws SaveItemFailedException
*/
protected void saveItem(final Item item, final String ownerName, final Connection connection,
final SaveResults saveResults) throws IOException
{
try
{
// switch based on the item type
switch (item.getItemType())
{
case GROUP:
saveGroup((Group) item, ownerName, connection, saveResults);
break;
case INDICATOR:
saveIndicator((Indicator) item, ownerName, connection, saveResults);
break;
default:
break;
}
}
catch (SaveItemFailedException e)
{
logger.warn(e.getMessage(), e);
// add this item to the list of failed saves
saveResults.addFailedItems(item);
// this item failed to save so attempt to save the associated items individually if they
// exist without the associations
SaveResults childItemsSaveResults = saveItems(item.getAssociatedItems(), connection);
saveResults.addFailedItems(childItemsSaveResults.getFailedItems());
}
}
/**
* Retrieves the specific writer implementation for this group
*
* @param group
* @param connection
* @return
*/
protected GroupWriter<?, ?> getGroupWriter(final Group group, final Connection connection)
{
GroupWriter<?, ?> writer = null;
// switch based on the indicator type
switch (group.getGroupType())
{
case ADVERSARY:
writer = new AdversaryWriter(connection, (Adversary) group);
break;
case DOCUMENT:
writer = new DocumentWriter(connection, (Document) group);
break;
case EMAIL:
writer = new EmailWriter(connection, (Email) group);
break;
case INCIDENT:
writer = new IncidentWriter(connection, (Incident) group);
break;
case SIGNATURE:
writer = new SignatureWriter(connection, (Signature) group);
break;
case THREAT:
writer = new ThreatWriter(connection, (Threat) group);
break;
default:
throw new IllegalArgumentException("invalid group type");
}
return writer;
}
/**
* Retrieves the specific writer implementation for this indicator
*
* @param indicator
* @param connection
* @return
*/
protected IndicatorWriter<?, ?> getIndicatorWriter(final Indicator indicator, final Connection connection)
{
IndicatorWriter<?, ?> writer = null;
// switch based on the indicator type
switch (indicator.getIndicatorType())
{
case Address.INDICATOR_TYPE:
writer = new AddressWriter(connection, (Address) indicator);
break;
case EmailAddress.INDICATOR_TYPE:
writer = new EmailAddressWriter(connection, (EmailAddress) indicator);
break;
case File.INDICATOR_TYPE:
writer = new FileWriter(connection, (File) indicator);
break;
case Host.INDICATOR_TYPE:
writer = new HostWriter(connection, (Host) indicator);
break;
case Url.INDICATOR_TYPE:
writer = new UrlWriter(connection, (Url) indicator);
break;
default:
//this must be a custom indicator type
writer = new CustomIndicatorWriter(connection, (CustomIndicator) indicator);
}
return writer;
}
protected com.threatconnect.sdk.server.entity.Group saveGroup(final Group group, final String ownerName,
final Connection connection, final SaveResults saveResults) throws IOException, SaveItemFailedException
{
GroupWriter<?, ?> writer = getGroupWriter(group, connection);
// save the group
com.threatconnect.sdk.server.entity.Group savedGroup = writer.saveGroup(ownerName);
// save the associated indicators for this group
saveAssociatedItems(group, ownerName, connection, writer, saveResults);
// return the saved group
return savedGroup;
}
protected com.threatconnect.sdk.server.entity.Indicator saveIndicator(final Indicator indicator,
final String ownerName, final Connection connection, final SaveResults saveResults)
throws IOException, SaveItemFailedException
{
IndicatorWriter<?, ?> writer = getIndicatorWriter(indicator, connection);
// save the indicator
com.threatconnect.sdk.server.entity.Indicator savedIndicator = writer.saveIndicator(ownerName);
// save the associated groups for this indicator
saveAssociatedGroups(indicator, ownerName, connection, writer, saveResults);
// return the saved indicator
return savedIndicator;
}
/**
* Saves the associated groups for a given indicator
*
* @param indicator
* @param ownerName
* @param connection
* @param writer
* @throws IOException
* Signals that an I/O exception of some sort has occurred. This
* class is the general class of exceptions produced by failed or
* interrupted I/O operations.
* @throws SaveItemFailedException
*/
protected void saveAssociatedGroups(final Indicator indicator, final String ownerName, final Connection connection,
IndicatorWriter<?, ?> writer, final SaveResults saveResults) throws IOException
{
// for each of the associated items of this group
for (Group associatedGroup : indicator.getAssociatedItems())
{
try
{
com.threatconnect.sdk.server.entity.Group savedAssociatedGroup =
saveGroup(associatedGroup, ownerName, connection, saveResults);
writer.associateGroup(associatedGroup.getGroupType(), savedAssociatedGroup.getId());
}
catch (SaveItemFailedException e)
{
logger.warn(e.getMessage(), e);
// add to the list of failed items
saveResults.addFailedItems(associatedGroup);
// this item failed to save so attempt to save the associated items individually if
// they exist without the associations
SaveResults childItemsSaveResults = saveItems(associatedGroup.getAssociatedItems(), connection);
saveResults.addFailedItems(childItemsSaveResults.getFailedItems());
}
catch (AssociateFailedException e)
{
logger.warn(e.getMessage(), e);
}
}
}
/**
* Saves the associated items for a given group
*
* @param group
* @param ownerName
* @param connection
* @param writer
* @param saveResults
* @throws IOException
* Signals that an I/O exception of some sort has occurred. This
* class is the general class of exceptions produced by failed or
* interrupted I/O operations.
*/
protected void saveAssociatedItems(final Group group, final String ownerName, final Connection connection,
GroupWriter<?, ?> writer, final SaveResults saveResults) throws IOException
{
// for each of the associated items of this group
for (Item associatedItem : group.getAssociatedItems())
{
try
{
// switch based on the item type
switch (associatedItem.getItemType())
{
case GROUP:
Group associatedGroup = (Group) associatedItem;
com.threatconnect.sdk.server.entity.Group savedAssociatedGroup =
saveGroup(associatedGroup, ownerName, connection, saveResults);
writer.associateGroup(associatedGroup.getGroupType(), savedAssociatedGroup.getId());
break;
case INDICATOR:
Indicator associatedIndicator = (Indicator) associatedItem;
saveIndicator(associatedIndicator, ownerName, connection, saveResults);
writer.associateIndicator(associatedIndicator);
break;
default:
break;
}
}
catch (SaveItemFailedException e)
{
logger.warn(e.getMessage(), e);
// add to the list of failed items
saveResults.addFailedItems(associatedItem);
// this item failed to save so attempt to save the associated items individually if
// they exist without the associations
SaveResults childItemsSaveResults = saveItems(associatedItem.getAssociatedItems(), connection);
saveResults.addFailedItems(childItemsSaveResults.getFailedItems());
}
catch (AssociateFailedException e)
{
logger.warn(e.getMessage(), e);
}
}
}
}