package biz.karms.sinkit.ejb.impl;
import biz.karms.sinkit.ejb.ArchiveService;
import biz.karms.sinkit.ejb.BlacklistCacheService;
import biz.karms.sinkit.ejb.CacheBuilder;
import biz.karms.sinkit.ejb.CoreService;
import biz.karms.sinkit.ejb.WhitelistCacheService;
import biz.karms.sinkit.ejb.cache.pojo.WhitelistedRecord;
import biz.karms.sinkit.ejb.util.IoCValidator;
import biz.karms.sinkit.ejb.util.WhitelistUtils;
import biz.karms.sinkit.exception.ArchiveException;
import biz.karms.sinkit.exception.IoCValidationException;
import biz.karms.sinkit.ioc.IoCRecord;
import biz.karms.sinkit.ioc.IoCSeen;
import biz.karms.sinkit.ioc.IoCSourceId;
import biz.karms.sinkit.ioc.IoCSourceIdType;
import biz.karms.sinkit.ioc.util.IoCSourceIdBuilder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Tomas Kozel
*/
@Stateless
public class CoreServiceEJB implements CoreService {
private static final String IOC_ACTIVE_HOURS_ENV = "SINKIT_IOC_ACTIVE_HOURS";
private static final String WHITELIST_VALID_HOURS_ENV = "SINKIT_WHITELIST_VALID_HOURS";
private int iocActiveHours;
private long whitelistValidSeconds;
@Inject
private Logger log;
@EJB
private ArchiveService archiveService;
@EJB
private BlacklistCacheService blacklistCacheService;
@EJB
private WhitelistCacheService whitelistCacheService;
@EJB
private CacheBuilder cacheBuilder;
@PostConstruct
public void setup() {
if (log == null || archiveService == null || blacklistCacheService == null || whitelistCacheService == null ||
cacheBuilder == null) {
throw new IllegalArgumentException("Logger, ArchiveServiceEJB, BlacklistCacheServiceEJB, " +
"WhitelistCacheServiceEJB, CacheBuilderEJB must be injected.");
}
try {
iocActiveHours = Integer.parseInt(System.getenv(IOC_ACTIVE_HOURS_ENV));
} catch (RuntimeException re) {
throw new IllegalArgumentException("System env " + IOC_ACTIVE_HOURS_ENV + " is invalid: " + System.getenv(IOC_ACTIVE_HOURS_ENV));
}
if (iocActiveHours < 0) {
throw new IllegalArgumentException("System env " + IOC_ACTIVE_HOURS_ENV + " is mandatory and must be integer bigger than zero.");
}
if (StringUtils.isBlank(System.getenv(WHITELIST_VALID_HOURS_ENV))) {
throw new IllegalStateException("System property '" + WHITELIST_VALID_HOURS_ENV + "' is not set");
}
try {
whitelistValidSeconds = Integer.parseInt(System.getenv(WHITELIST_VALID_HOURS_ENV)) * 3600;
} catch (NumberFormatException ex) {
throw new IllegalStateException("System property '" + WHITELIST_VALID_HOURS_ENV + "' is not valid integer", ex);
}
}
public int getIocActiveHours() {
return iocActiveHours;
}
@Override
public IoCRecord processIoCRecord(final IoCRecord ioc) throws ArchiveException, IoCValidationException {
// validate ioc
IoCValidator.validateIoCRecord(ioc, iocActiveHours);
// try to construct source ID
ioc.getSource().setId(IoCSourceIdBuilder.build(ioc));
ioc.setActive(true);
Date seenFirst;
Date seenLast;
if (ioc.getTime().getSource() == null) {
seenFirst = ioc.getTime().getObservation();
seenLast = ioc.getTime().getObservation();
} else {
seenFirst = ioc.getTime().getSource();
seenLast = ioc.getTime().getSource();
}
IoCSeen seen = new IoCSeen();
seen.setLast(seenLast);
seen.setFirst(seenFirst);
ioc.setSeen(seen);
Date receivedByCore = Calendar.getInstance().getTime();
ioc.getTime().setReceivedByCore(receivedByCore);
WhitelistedRecord white = null;
if (ioc.getSource().getId().getType() == IoCSourceIdType.FQDN) {
final String[] fqdns = WhitelistUtils.explodeDomains(ioc.getSource().getId().getValue());
int i = 0;
while (i < fqdns.length && white == null) {
white = whitelistCacheService.get(fqdns[i++]);
}
} else {
white = whitelistCacheService.get(ioc.getSource().getId().getValue());
}
if (white == null) {
// ioc is inserted as is if does not exist or last.seen is updated
// ioc has to be inserted before blacklist record, because it needs the documentId to be computed
archiveService.archiveReceivedIoCRecord(ioc);
blacklistCacheService.addToCache(ioc);
} else {
ioc.setWhitelistName(white.getSourceName());
ioc.getTime().setWhitelisted(receivedByCore);
archiveService.archiveReceivedIoCRecord(ioc);
}
return ioc;
}
@Override
public int deactivateIocs() throws ArchiveException {
log.info("Deactivation job started");
int deactivated = 0;
List<IoCRecord> iocs;
// due to archiveService.findIoCsForDeactivation has limit for single search (i.e. max 1000 records)
// this has to be done in multiple runs until search returns 0 results
do {
iocs = archiveService.findIoCsForDeactivation(iocActiveHours);
if (!CollectionUtils.isEmpty(iocs)) {
for (IoCRecord ioc : iocs) {
blacklistCacheService.removeFromCache(ioc);
archiveService.deactivateRecord(ioc);
}
deactivated += iocs.size();
}
} while (iocs.size() > 0);
if (deactivated == 0) {
log.info("No IoCs for deactivation found. Ending job...");
} else {
log.info("IoCs deactivated: " + deactivated);
}
return deactivated;
}
@Override
public boolean runCacheRebuilding() {
if (cacheBuilder.isCacheRebuildRunning()) {
log.info("Cache rebuilding still in process -> skipping");
return false;
}
cacheBuilder.runCacheRebuilding();
return true;
}
@Override
public void enrich() {
throw new UnsupportedOperationException("VirusTotal enricher is handled by Clustered HA Singleton Timer Service. This API call is currently disabled.");
}
@Override
public boolean processWhitelistIoCRecord(final IoCRecord whiteIoC) throws IoCValidationException, ArchiveException {
IoCValidator.validateWhitelistIoCRecord(whiteIoC);
final IoCSourceId sid = IoCSourceIdBuilder.build(whiteIoC);
whiteIoC.getSource().setId(sid);
// TODO: remove when ttl is received from IntelMQ
whiteIoC.getSource().setTTL(whitelistValidSeconds);
WhitelistedRecord white = whitelistCacheService.get(whiteIoC.getSource().getId().getValue());
boolean putToCacheBefore = true;
if (white != null) {
final Calendar expiresAt = Calendar.getInstance();
expiresAt.add(Calendar.SECOND, whiteIoC.getSource().getTTL().intValue());
// if old whitelist record needs update
if (expiresAt.after(white.getExpiresAt())) {
// if old whitelist record was completely processed during last run just update it and quit the process
if (white.isCompleted()) {
if (whitelistCacheService.put(whiteIoC, true) == null) {
log.log(Level.SEVERE, "Cannot update whitelist record. Aborting process...");
return false;
} else {
return true;
}
} else { // else the whitelisted record will be updated outside these 'ifs'
putToCacheBefore = true;
}
} else { // else old whitelist record doesn't need update
// if old whitelist record was completely processed during last run and doesn't need update
// then nothing else has to be done
if (white.isCompleted()) {
return true;
} else {
putToCacheBefore = false;
}
}
}
if (putToCacheBefore) {
white = whitelistCacheService.put(whiteIoC, false);
if (white == null) {
log.log(Level.SEVERE, "Cannot put whitelist record to whitelist cache. Aborting process...");
return false;
}
}
final List<IoCRecord> iocs = archiveService.findIoCsForWhitelisting(white.getRawId());
for (IoCRecord iocRecord : iocs) {
if (!blacklistCacheService.removeWholeObjectFromCache(iocRecord)) {
return false;
}
archiveService.setRecordWhitelisted(iocRecord, white.getSourceName());
}
return whitelistCacheService.setCompleted(white) != null;
}
@Override
public WhitelistedRecord getWhitelistedRecord(String id) {
if (id == null) {
log.log(Level.SEVERE, "getWhitelistedRecord: cannot search whitelist, id is null");
return null;
}
return whitelistCacheService.get(id);
}
@Override
public boolean removeWhitelistedRecord(String id) {
if (id == null) {
log.log(Level.SEVERE, "removeWhitelistedRecord: cannot remove whitelist entry, id is null");
return false;
}
return false;
}
@Override
public boolean isWhitelistEmpty() {
return whitelistCacheService.isWhitelistEmpty();
}
@Override
public void setWhitelistValidSeconds(long whitelistValidSeconds) {
this.whitelistValidSeconds = whitelistValidSeconds;
}
}