diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index 8485a9cfc3..77c206e1db 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -226,8 +226,8 @@ final class DataSourceInfoUtilities { */ private DataSourceInfoUtilities() { } - - /** + + /** * Create a Map of lists of artifacts sorted by the given attribute. * * @param skCase SleuthkitCase instance. @@ -277,12 +277,12 @@ final class DataSourceInfoUtilities { List artifactList = new ArrayList<>(); for (List mapArtifactList : sortedMap.values()) { - + if (maxCount == 0 || (artifactList.size() + mapArtifactList.size()) <= maxCount) { artifactList.addAll(mapArtifactList); continue; } - + if (maxCount == artifactList.size()) { break; } @@ -359,9 +359,16 @@ final class DataSourceInfoUtilities { } } } - - - + + /** + * Retrieves attribute from artifact if exists. Returns null if attribute is + * null or underlying call throws exception. + * + * @param artifact The artifact. + * @param attributeType The attribute type to retrieve from the artifact. + * + * @return The attribute or null if could not be received. + */ private static BlackboardAttribute getAttributeOrNull(BlackboardArtifact artifact, Type attributeType) { try { return artifact.getAttribute(attributeType); @@ -369,17 +376,45 @@ final class DataSourceInfoUtilities { return null; } } - + + /** + * Retrieves the string value of a certain attribute type from an artifact. + * + * @param artifact The artifact. + * @param attributeType The attribute type. + * + * @return The 'getValueString()' value or null if the attribute or String + * could not be retrieved. + */ static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) { BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); return (attr == null) ? null : attr.getValueString(); } - + + /** + * Retrieves the long value of a certain attribute type from an artifact. + * + * @param artifact The artifact. + * @param attributeType The attribute type. + * + * @return The 'getValueLong()' value or null if the attribute could not be + * retrieved. + */ static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) { BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); return (attr == null) ? null : attr.getValueLong(); } - + + /** + * Retrieves the long value of a certain attribute type from an artifact and + * converts to date (seconds since epoch). + * + * @param artifact The artifact. + * @param attributeType The attribute type. + * + * @return The date determined from the 'getValueLong()' as seconds from + * epoch or null if the attribute could not be retrieved. + */ static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) { Long longVal = getLongOrNull(artifact, attributeType); return (longVal == null) ? null : new Date(longVal * 1000); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java index 86acbdd9a8..2cc7374218 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java @@ -42,10 +42,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DAT import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; /** @@ -61,17 +58,13 @@ public class DataSourceUserActivitySummary { private static final BlackboardAttribute.Type TYPE_DEVICE_ID = new BlackboardAttribute.Type(TSK_DEVICE_ID); private static final BlackboardAttribute.Type TYPE_DEVICE_MAKE = new BlackboardAttribute.Type(TSK_DEVICE_MAKE); private static final BlackboardAttribute.Type TYPE_DEVICE_MODEL = new BlackboardAttribute.Type(TSK_DEVICE_MODEL); - private static final BlackboardAttribute.Type TYPE_MAC_ADDRESS = new BlackboardAttribute.Type(TSK_MAC_ADDRESS); private static final BlackboardAttribute.Type TYPE_MESSAGE_TYPE = new BlackboardAttribute.Type(TSK_MESSAGE_TYPE); private static final BlackboardAttribute.Type TYPE_TEXT = new BlackboardAttribute.Type(TSK_TEXT); - private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(TSK_DOMAIN); - private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(TSK_PROG_NAME); - private static final Comparator TOP_ACCOUNT_RESULT_DATE_COMPARE = (a,b) -> a.getLastAccess().compareTo(b.getLastAccess()); - private static final Comparator TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a,b) -> a.getDateAccessed().compareTo(b.getDateAccessed()); + private static final Comparator TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess()); + private static final Comparator TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed()); private static final String ROOT_HUB_IDENTIFIER = "ROOT_HUB"; - - + private static final long SLEEP_TIME = 5000; /** @@ -110,22 +103,58 @@ public class DataSourceUserActivitySummary { private final TextTranslationService translationService; private final java.util.logging.Logger logger; + /** + * Main constructor. + */ public DataSourceUserActivitySummary() { this(SleuthkitCaseProvider.DEFAULT, TextTranslationService.getInstance(), org.sleuthkit.autopsy.coreutils.Logger.getLogger(DataSourceUserActivitySummary.class.getName())); } + /** + * Main constructor with external dependencies specified. This constructor + * is designed with unit testing in mind since mocked dependencies can be + * utilized. + * + * @param provider The object providing the current SleuthkitCase. + * @param translationService The translation service. + * @param logger The logger to use. + */ public DataSourceUserActivitySummary(SleuthkitCaseProvider provider, TextTranslationService translationService, java.util.logging.Logger logger) { this.caseProvider = provider; this.translationService = translationService; this.logger = logger; } - public List getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + /** + * Throws an IllegalArgumentException if count <= 0. + * + * @param count The count being checked. + */ + private void assertValidCount(int count) { if (count <= 0) { throw new IllegalArgumentException("Count must be greater than 0"); } - + } + + /** + * Retrieves most recent web searches by most recent date grouped by search + * term. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent web searches where most recent search + * appears first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + assertValidCount(count); + List results = caseProvider.get().getBlackboard().getArtifacts(TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId()) .stream() // get items where search string and date is not null @@ -135,12 +164,10 @@ public class DataSourceUserActivitySummary { if (StringUtils.isBlank(searchString) || dateAccessed == null) { return null; } - + return new TopWebSearchResult( - searchString, - dateAccessed, - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DOMAIN), - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME) + searchString, + dateAccessed ); }) // remove null records @@ -157,7 +184,6 @@ public class DataSourceUserActivitySummary { // get as list .collect(Collectors.toList()); - // get translation if possible for (TopWebSearchResult result : results) { if (StringUtils.isNotBlank(result.getSearchString()) && translationService.hasProvider()) { @@ -177,29 +203,59 @@ public class DataSourceUserActivitySummary { return results; } - - + /** + * Retrieves most recent devices used by most recent date attached. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent devices attached where most recent device + * attached appears first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ public List getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + assertValidCount(count); + return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED, dataSource, TYPE_DATETIME, SortOrder.DESCENDING, 0) .stream() - .map(artifact -> new TopDeviceAttachedResult( - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID), - DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME), - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE), - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL), - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MAC_ADDRESS) - )) - .filter(result -> result.getDeviceModel() == null || !result.getDeviceModel().trim().toUpperCase().equals(ROOT_HUB_IDENTIFIER)) + .map(artifact -> { + return new TopDeviceAttachedResult( + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID), + DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL) + ); + }) + // remove Root Hub identifier + .filter(result -> result.getDeviceModel() == null + || !result.getDeviceModel().trim().toUpperCase().equals(ROOT_HUB_IDENTIFIER)) .limit(count) .collect(Collectors.toList()); } - + + /** + * Retrieves most recent account used by most recent date for a message + * sent. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent accounts used where the most recent + * account by last message sent occurs first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ public List getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { - if (count <= 0) { - throw new IllegalArgumentException("Count must be greater than 0"); - } - + assertValidCount(count); + return caseProvider.get().getBlackboard().getArtifacts(TSK_MESSAGE.getTypeID(), dataSource.getId()) .stream() // get message type and date (or null if one of those attributes does not exist) @@ -224,97 +280,141 @@ public class DataSourceUserActivitySummary { .collect(Collectors.toList()); } + /** + * Object containing information about a web search artifact. + */ public static class TopWebSearchResult { private final String searchString; private final Date dateAccessed; - private final String domain; - private final String programName; private String translatedResult; - public TopWebSearchResult(String searchString, Date dateAccessed, String domain, String programName) { + /** + * Main constructor. + * + * @param searchString The search string. + * @param dateAccessed The latest date searched. + */ + public TopWebSearchResult(String searchString, Date dateAccessed) { this.searchString = searchString; this.dateAccessed = dateAccessed; - this.domain = domain; - this.programName = programName; } + /** + * @return The translated result if one was determined. + */ public String getTranslatedResult() { return translatedResult; } + /** + * Sets the translated result for this web search. + * + * @param translatedResult The translated result. + */ public void setTranslatedResult(String translatedResult) { this.translatedResult = translatedResult; } + /** + * @return The search string. + */ public String getSearchString() { return searchString; } + /** + * @return The date for the search. + */ public Date getDateAccessed() { return dateAccessed; } - - public String getDomain() { - return domain; - } - - public String getProgramName() { - return programName; - } } + /** + * A record of a device attached. + */ public static class TopDeviceAttachedResult { private final String deviceId; private final Date dateAccessed; private final String deviceMake; private final String deviceModel; - private final String macAddress; - public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, String macAddress) { + /** + * Main constructor. + * + * @param deviceId The device id. + * @param dateAccessed The date last attached. + * @param deviceMake The device make. + * @param deviceModel The device model. + */ + public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) { this.deviceId = deviceId; this.dateAccessed = dateAccessed; this.deviceMake = deviceMake; this.deviceModel = deviceModel; - this.macAddress = macAddress; } + /** + * @return The device id. + */ public String getDeviceId() { return deviceId; } + /** + * @return The date last attached. + */ public Date getDateAccessed() { return dateAccessed; } + /** + * @return The device make. + */ public String getDeviceMake() { return deviceMake; } + /** + * @return The device model. + */ public String getDeviceModel() { return deviceModel; } - - public String getMacAddress() { - return macAddress; - } } + /** + * A record of an account and the last time it was used determined by + * messages. + */ public static class TopAccountResult { private final String accountType; private final Date lastAccess; + /** + * Main constructor. + * + * @param accountType The account type. + * @param lastAccess The date the account was last accessed. + */ public TopAccountResult(String accountType, Date lastAccess) { this.accountType = accountType; this.lastAccess = lastAccess; } + /** + * @return The account type. + */ public String getAccountType() { return accountType; } + /** + * @return The date the account was last accessed. + */ public Date getLastAccess() { return lastAccess; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index ff01271c63..febc4f338f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -37,7 +37,6 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivityS import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.HorizontalAlign; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; @@ -50,28 +49,22 @@ import org.sleuthkit.datamodel.DataSource; */ @Messages({ "DataSourceSummaryUserActivityPanel_tab_title=User Activity", - "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run", - "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access", "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists", - "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header=Search String", "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed", "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated", - "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id", "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model", "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed", - "DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type", - "DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed", -}) + "DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed",}) public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; @@ -85,8 +78,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan private static String getFormatted(Date date) { return date == null ? "" : DATETIME_FORMAT.format(date); } - - + private final JTablePanel topProgramsTable; private final JTablePanel recentDomainsTable; private final JTablePanel topWebSearchesTable; @@ -152,13 +144,11 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), (recentDomain) -> new DefaultCellModel(recentDomain.getDomain()), 250), - // url column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(), (recentDomain) -> new DefaultCellModel(recentDomain.getUrl()), 250), - // last accessed column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(), @@ -208,15 +198,15 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan (device) -> { String make = StringUtils.isBlank(device.getDeviceMake()) ? "" : device.getDeviceMake().trim(); String model = StringUtils.isBlank(device.getDeviceModel()) ? "" : device.getDeviceModel().trim(); - String makeModelString = (make.isEmpty() || model.isEmpty()) ? - make + model : - String.format("%s - %s", make, model); + String makeModelString = (make.isEmpty() || model.isEmpty()) + ? make + model + : String.format("%s - %s", make, model); return new DefaultCellModel(makeModelString); }, 250 ) )); - + // top accounts table this.topAccountsTable = JTablePanel.getJTablePanel(Arrays.asList( // account type column @@ -232,8 +222,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan 150 ) )); - - + this.tables = Arrays.asList( topProgramsTable, recentDomainsTable, diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 70e5a0c3ca..aeb29b9fe9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -83,7 +83,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * @return The horizontal alignment for the text in the cell. */ HorizontalAlign getHorizontalAlignment(); - + /** * @return The insets for the cell text. */ @@ -147,15 +147,17 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { this.horizontalAlignment = alignment; return this; } - + @Override public Insets getInsets() { return insets; } - + /** * Sets the insets for the text within the cell + * * @param insets The insets. + * * @return As a utility, returns this. */ public DefaultCellModel setInsets(Insets insets) { @@ -170,7 +172,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { } private static final int DEFAULT_ALIGNMENT = JLabel.LEFT; - private static final Border DEFAULT_BORDER = BorderFactory.createEmptyBorder(1,5,1,5); + private static final Border DEFAULT_BORDER = BorderFactory.createEmptyBorder(1, 5, 1, 5); @Override public Component getTableCellRendererComponent(JTable table, Object value, @@ -218,7 +220,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { } else { defaultCell.setBorder(DEFAULT_BORDER); } - + // sets the JLabel alignment (left, center, right) or default alignment // if no alignment is specified int alignment = (cellModel.getHorizontalAlignment() == null)