From 5022d5ef2498dc0322e6d2cdfc43b68dc9e69425 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 11 Nov 2021 09:30:53 -0500 Subject: [PATCH 01/11] First cut --- .../mainui/datamodel/CommAccountsDAO.java | 327 ++++++++++++++++++ .../datamodel/CommAccountsSearchParams.java | 84 +++++ .../autopsy/mainui/datamodel/MainDAO.java | 9 +- 3 files changed, 418 insertions(+), 2 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java create mode 100755 Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java new file mode 100755 index 0000000000..3902175802 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -0,0 +1,327 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.mainui.datamodel; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import java.beans.PropertyChangeEvent; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; +import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.DataArtifact; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Provides information to populate the results viewer for data in the + * Communication Accounts section. + */ +@Messages({"CommAccountsDAO.fileColumns.originalName=Source Name", + "CommAccountsDAO.fileColumns.noDescription=No Description"}) +public class CommAccountsDAO { + + private static final int CACHE_SIZE = Account.Type.PREDEFINED_ACCOUNT_TYPES.size(); // number of cached SearchParams sub-types + private static final long CACHE_DURATION = 2; + private static final TimeUnit CACHE_DURATION_UNITS = TimeUnit.MINUTES; + private final Cache, SearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build(); + + private static final List FILE_TAG_COLUMNS = Arrays.asList( + getFileColumnKey(Bundle.CommAccountsDAO_fileColumns_originalName()) // GVDTODO handle translation + ); + + private static CommAccountsDAO instance = null; + + synchronized static CommAccountsDAO getInstance() { + if (instance == null) { + instance = new CommAccountsDAO(); + } + + return instance; + } + + private static ColumnKey getFileColumnKey(String name) { + return new ColumnKey(name, name, Bundle.CommAccountsDAO_fileColumns_noDescription()); + } + + public SearchResultsDTO getCommAcounts(CommAccountsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException { + if (key.getType() == null) { + throw new IllegalArgumentException("Must have non-null type"); + } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) { + throw new IllegalArgumentException("Data source id must be greater than 0 or null"); + } + + SearchParams searchParams = new SearchParams<>(key, startItem, maxCount); + if (hardRefresh) { + this.searchParamsCache.invalidate(searchParams); + } + + return searchParamsCache.get(searchParams, () -> fetchCommAccountsDTOs(searchParams)); + } + + /** + * Returns a list of paged communication accounts results. + * + * @param accts The communication accounts results. + * @param searchParams The search parameters including the paging. + * + * @return The list of paged communication accounts results. + */ + List getPaged(List accts, SearchParams searchParams) { + Stream pagedAcctsStream = accts.stream() + .sorted(Comparator.comparing((acct) -> acct.getAccountID())) + .skip(searchParams.getStartItem()); + + if (searchParams.getMaxResultsCount() != null) { + pagedAcctsStream = pagedAcctsStream.limit(searchParams.getMaxResultsCount()); + } + + return pagedAcctsStream.collect(Collectors.toList()); + } + + /** + * Get the clause that should be used in order to (not) filter out rejected + * results from db queries. + * + * @return A clause that will or will not filter out rejected artifacts + * based on the state of showRejected. + */ + private String getRejectedArtifactFilterClause(boolean showRejected) { + return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS + } + + /** + * Returns the clause to filter artifacts by data source. + * + * @return A clause that will or will not filter artifacts by datasource + * based on the CasePreferences groupItemsInTreeByDataSource setting + */ + private String getFilterByDataSourceClause(Long dataSourceId) { + if (dataSourceId != null && dataSourceId > 0) { + return " AND blackboard_artifacts.data_source_obj_id = " + dataSourceId + " "; + } + return " "; + } + + String getWhereClause(SearchParams cacheKey) { + Long dataSourceId = cacheKey.getParamData().getDataSourceId(); + Account.Type type = cacheKey.getParamData().getType(); + + String originalWhereClause + = " blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS + + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS + + " AND blackboard_attributes.value_text = '" + type + "'" //NON-NLS + + getFilterByDataSourceClause(dataSourceId) + + getRejectedArtifactFilterClause(false); // ELTODO + + String pagedWhereClause = originalWhereClause + + " ORDER BY artifacts.obj_id ASC" + + (cacheKey.getMaxResultsCount() != null && cacheKey.getMaxResultsCount() > 0 ? " LIMIT " + cacheKey.getMaxResultsCount() : "") + + (cacheKey.getStartItem() > 0 ? " OFFSET " + cacheKey.getStartItem() : ""); + return pagedWhereClause; + } + + long getTotalResultsCount(SearchParams cacheKey, long currentPageSize) throws TskCoreException, NoCurrentCaseException { + Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard(); + Long dataSourceId = cacheKey.getParamData().getDataSourceId(); + BlackboardArtifact.Type artType = BlackboardArtifact.Type.TSK_ACCOUNT; + + if ( (cacheKey.getStartItem() == 0) // offset is zero AND + && ( (cacheKey.getMaxResultsCount() != null && currentPageSize < cacheKey.getMaxResultsCount()) // number of results is less than max + || (cacheKey.getMaxResultsCount() == null)) ) { // OR max number of results was not specified + return currentPageSize; + } else { + if (dataSourceId != null) { + return blackboard.getArtifactsCount(artType.getTypeID(), dataSourceId); + } else { + return blackboard.getArtifactsCount(artType.getTypeID()); + } + } + } + + @NbBundle.Messages({"CommAccounts.name.text=Communication Accounts"}) + private SearchResultsDTO fetchCommAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { + + // get current page of communication accounts results + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + Blackboard blackboard = skCase.getBlackboard(); + + String pagedWhereClause = getWhereClause(cacheKey); + + List arts = new ArrayList<>(); + arts.addAll(blackboard.getDataArtifactsWhere(pagedWhereClause)); + blackboard.loadBlackboardAttributes(arts); + + long totalResultsCount = getTotalResultsCount(cacheKey, arts.size()); + BlackboardArtifactDAO.TableData tableData = DataArtifactDAO.getInstance().createTableData(BlackboardArtifact.Type.TSK_ACCOUNT, arts); + return new DataArtifactTableSearchResultsDTO(BlackboardArtifact.Type.TSK_ACCOUNT, tableData.columnKeys, tableData.rows, cacheKey.getStartItem(), totalResultsCount); + + /*List fileRows = new ArrayList<>(); + for (DataArtifact account : arts) { + Account blackboardTag = (Account) account; + + String name = blackboardTag.getContent().getName(); // As a backup. + try { + name = blackboardTag.getArtifact().getShortDescription(); + } catch (TskCoreException ignore) { + // it's a WARNING, skip + } + + String contentPath; + try { + contentPath = blackboardTag.getContent().getUniquePath(); + } catch (TskCoreException ex) { + contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text"); + } + + List cellValues = Arrays.asList(name, + null, // GVDTODO translation column + contentPath, + blackboardTag.getArtifact().getDisplayName(), + blackboardTag.getComment(), + blackboardTag.getUserName()); + + fileRows.add(new BlackboardArtifactTagsRowDTO( + blackboardTag, + cellValues, + blackboardTag.getId())); + } + + return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());*/ + } + + /** + * Handles fetching and paging of data for communication accounts. + */ + public static class CommAccountFetcher extends DAOFetcher { + + /** + * Main constructor. + * + * @param params Parameters to handle fetching of data. + */ + public CommAccountFetcher(CommAccountsSearchParams params) { + super(params); + } + + @Override + public SearchResultsDTO getSearchResults(int pageSize, int pageIdx, boolean hardRefresh) throws ExecutionException { + return MainDAO.getInstance().getCommAccountsDAO().getCommAcounts(this.getParameters(), pageIdx * pageSize, (long) pageSize, hardRefresh); + } + + @Override + public boolean isRefreshRequired(PropertyChangeEvent evt) { + CommAccountsSearchParams params = this.getParameters(); + String eventType = evt.getPropertyName(); + /* + // handle artifact/result account changes + if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString()) + || eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) { + + // ignore non-artifact/result account changes + if (params.getTagType() != TagsSearchParams.TagType.RESULT) { + return false; + } + + if (evt instanceof AutopsyEvent) { + if (evt instanceof BlackBoardArtifactTagAddedEvent) { + // An artifact associated with the current case has been tagged. + BlackBoardArtifactTagAddedEvent event = (BlackBoardArtifactTagAddedEvent) evt; + // ensure account added event has a valid content id + if (event.getAddedTag() == null || event.getAddedTag().getContent() == null || event.getAddedTag().getArtifact() == null) { + return false; + } + return params.getTagName().getId() == event.getAddedTag().getId(); + } else if (evt instanceof BlackBoardArtifactTagDeletedEvent) { + // A account has been removed from an artifact associated with the current case. + BlackBoardArtifactTagDeletedEvent event = (BlackBoardArtifactTagDeletedEvent) evt; + // ensure account deleted event has a valid content id + BlackBoardArtifactTagDeletedEvent.DeletedBlackboardArtifactTagInfo deletedTagInfo = event.getDeletedTagInfo(); + if (deletedTagInfo == null) { + return false; + } + return params.getTagName().getId() == deletedTagInfo.getTagID(); + } + } + } + + // handle file/content account changes + if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString()) + || eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { + + // ignore non-file/content account changes + if (params.getTagType() != TagsSearchParams.TagType.FILE) { + return false; + } + + if (evt instanceof AutopsyEvent) { + if (evt instanceof ContentTagAddedEvent) { + // Content associated with the current case has been tagged. + ContentTagAddedEvent event = (ContentTagAddedEvent) evt; + // ensure account added event has a valid content id + if (event.getAddedTag() == null || event.getAddedTag().getContent() == null) { + return false; + } + return params.getTagName().getId() == event.getAddedTag().getId(); + } else if (evt instanceof ContentTagDeletedEvent) { + // A account has been removed from content associated with the current case. + ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; + // ensure account deleted event has a valid content id + ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = event.getDeletedTagInfo(); + if (deletedTagInfo == null) { + return false; + } + return params.getTagName().getId() == deletedTagInfo.getTagID(); + } + } + }*/ + return false; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java new file mode 100755 index 0000000000..153f4557ad --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java @@ -0,0 +1,84 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.mainui.datamodel; + +import java.util.Objects; +import org.sleuthkit.datamodel.Account; + +/** + * Key for accessing data about communication accounts from the DAO. + */ +public class CommAccountsSearchParams { + + private final Account.Type type; + private final long account_id; // ELTODO do we need this? + private final Long dataSourceId; + + public CommAccountsSearchParams(long account_id, Account.Type type, Long dataSourceId) { + this.account_id = account_id; + this.type = type; + this.dataSourceId = dataSourceId; + } + + public long getAccountId() { + return account_id; + } + + public Account.Type getType() { + return type; + } + + public Long getDataSourceId() { + return dataSourceId; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.account_id); + hash = 23 * hash + Objects.hashCode(this.type); + hash = 23 * hash + Objects.hashCode(this.dataSourceId); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CommAccountsSearchParams other = (CommAccountsSearchParams) obj; + if (this.account_id != other.account_id) { + return false; + } + if (!Objects.equals(this.dataSourceId, other.dataSourceId)) { + return false; + } + if (!Objects.equals(this.type, other.type)) { + return false; + } + return true; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java index 81a21ef749..56a65af4ec 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java @@ -39,7 +39,8 @@ public class MainDAO { private final ViewsDAO viewsDAO = ViewsDAO.getInstance(); private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance(); private final TagsDAO tagsDAO = TagsDAO.getInstance(); - private final OsAccountsDAO accountsDAO = OsAccountsDAO.getInstance(); + private final OsAccountsDAO osAccountsDAO = OsAccountsDAO.getInstance(); + private final CommAccountsDAO commAccountsDAO = CommAccountsDAO.getInstance(); public DataArtifactDAO getDataArtifactsDAO() { return dataArtifactDAO; @@ -62,6 +63,10 @@ public class MainDAO { } public OsAccountsDAO getOsAccountsDAO() { - return accountsDAO; + return osAccountsDAO; + } + + public CommAccountsDAO getCommAccountsDAO() { + return commAccountsDAO; } } From 218d31b994a31dc388b03dca9ceb93829a33a011 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 11 Nov 2021 16:41:33 -0500 Subject: [PATCH 02/11] Making a separate query to get all artifacts --- .../mainui/datamodel/CommAccountsDAO.java | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index 3902175802..c6135f2f0f 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -33,6 +33,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -133,7 +134,7 @@ public class CommAccountsDAO { * based on the state of showRejected. */ private String getRejectedArtifactFilterClause(boolean showRejected) { - return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS + return showRejected ? " " : " AND artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; // } /** @@ -144,19 +145,38 @@ public class CommAccountsDAO { */ private String getFilterByDataSourceClause(Long dataSourceId) { if (dataSourceId != null && dataSourceId > 0) { - return " AND blackboard_artifacts.data_source_obj_id = " + dataSourceId + " "; + return " AND artifacts.data_source_obj_id = " + dataSourceId + " "; } return " "; - } - + } + + private final static String COMMUNICATION_ACCOUNTS_QUERY_STRING = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS + + "artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, " //NON-NLS + + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS + + " artifacts.review_status_id AS review_status_id, " //NON-NLS + + " data_artifacts.os_account_obj_id as os_account_obj_id " //NON-NLS + + " FROM blackboard_artifacts AS artifacts " + + " JOIN blackboard_artifact_types AS types " //NON-NLS + + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS + + " LEFT JOIN tsk_data_artifacts AS data_artifacts " + + " ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id " //NON-NLS + + " JOIN blackboard_attributes ON artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS + + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID(); //NON-NLS + + /** + * Get all artifacts and their attributes for all communication accounts of the type of interest. + * + * @param cacheKey + * @return + */ String getWhereClause(SearchParams cacheKey) { Long dataSourceId = cacheKey.getParamData().getDataSourceId(); Account.Type type = cacheKey.getParamData().getType(); String originalWhereClause - = " blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS - + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS - + " AND blackboard_attributes.value_text = '" + type + "'" //NON-NLS + = COMMUNICATION_ACCOUNTS_QUERY_STRING + + " AND blackboard_attributes.value_text = '" + type.getTypeName() + "'" //NON-NLS + getFilterByDataSourceClause(dataSourceId) + getRejectedArtifactFilterClause(false); // ELTODO @@ -186,23 +206,29 @@ public class CommAccountsDAO { } @NbBundle.Messages({"CommAccounts.name.text=Communication Accounts"}) - private SearchResultsDTO fetchCommAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { + private SearchResultsDTO fetchCommAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException, SQLException { // get current page of communication accounts results SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); Blackboard blackboard = skCase.getBlackboard(); + String pagedWhereClause = getWhereClause(cacheKey); + + List list = new ArrayList<>(); + try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(pagedWhereClause); + ResultSet rs = results.getResultSet();) { + List tempList = new ArrayList<>(); + while (rs.next()) { + tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS + } + for (Long artID : tempList) { + list.add(Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getDataArtifactById(artID)); + } + } - String pagedWhereClause = getWhereClause(cacheKey); - - List arts = new ArrayList<>(); - arts.addAll(blackboard.getDataArtifactsWhere(pagedWhereClause)); - blackboard.loadBlackboardAttributes(arts); - + /* long totalResultsCount = getTotalResultsCount(cacheKey, arts.size()); - BlackboardArtifactDAO.TableData tableData = DataArtifactDAO.getInstance().createTableData(BlackboardArtifact.Type.TSK_ACCOUNT, arts); - return new DataArtifactTableSearchResultsDTO(BlackboardArtifact.Type.TSK_ACCOUNT, tableData.columnKeys, tableData.rows, cacheKey.getStartItem(), totalResultsCount); - /*List fileRows = new ArrayList<>(); + List fileRows = new ArrayList<>(); for (DataArtifact account : arts) { Account blackboardTag = (Account) account; @@ -233,7 +259,7 @@ public class CommAccountsDAO { blackboardTag.getId())); } - return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());*/ + return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());* } /** From 67b89ce858d1f7e86ad29c2e654aaa55b12f83ab Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 11 Nov 2021 16:41:48 -0500 Subject: [PATCH 03/11] First cut at functional tests --- .../mainui/datamodel/TableSearchTest.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java index a96aac8bfb..b0cc895714 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java @@ -37,6 +37,8 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.TestUtilsException; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.Blackboard.BlackboardException; @@ -126,8 +128,16 @@ public class TableSearchTest extends NbTestCase { // OS Accounts test private static final String REALM_NAME_COLUMN = "Realm Name"; - private static final String HOST_COLUMN = "Host"; + private static final String HOST_COLUMN = "Host"; + // Communications accounts test + private static final String EMAIL_A = "AAA@yahoo.com"; + private static final String EMAIL_B = "BBB@gmail.com"; + private static final String EMAIL_C = "CCCCC@funmail.com"; + + private static final String PHONENUM_1 = "111 777 1111"; + private static final String PHONENUM_2 = "222 333 7777"; + ///////////////////////////////////////////////// // Data to be used across the test methods. // These are initialized in setUpCaseDatabase(). @@ -212,6 +222,7 @@ public class TableSearchTest extends NbTestCase { fileSystemTest(); tagsTest(); OsAccountsTest(); + commAccountsSearchTest(); } /** @@ -494,6 +505,13 @@ public class TableSearchTest extends NbTestCase { osAccount1 = accountMgr.newWindowsOsAccount("S-1-5-21-647283-46237-100", null, null, host2, OsAccountRealm.RealmScope.DOMAIN); accountMgr.newOsAccountInstance(osAccount1, dataSource2, OsAccountInstance.OsAccountInstanceType.LAUNCHED); + // Add communication accounts + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_A, "Test Module", fileA1); + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_B, "Test Module", fileA2); + AccountFileInstance deviceAccount_2 = openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, "devId1", "Test Module", fileA2); + + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_C, "Test Module", customFile); + } catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException | OsAccountManager.NotUserSIDException ex) { if (trans != null) { try { @@ -608,6 +626,39 @@ public class TableSearchTest extends NbTestCase { Assert.fail(ex.getMessage()); } } + + public void commAccountsSearchTest() { + // Quick test that everything is initialized + assertTrue(db != null); + + try { + // Get all contacts + CommAccountsDAO commAccountsDAO = MainDAO.getInstance().getCommAccountsDAO(); + + CommAccountsSearchParams param = new CommAccountsSearchParams(0, Account.Type.EMAIL, null); + SearchResultsDTO results = commAccountsDAO.getCommAcounts(param, 0, null, false); + //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); + assertEquals(3, results.getTotalResultsCount()); + assertEquals(3, results.getItems().size()); + + // Get contacts from data source 2 + param = new CommAccountsSearchParams(0, Account.Type.DEVICE, dataSource1.getId()); + results = commAccountsDAO.getCommAcounts(param, 0, null, false); + //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); + assertEquals(1, results.getTotalResultsCount()); + assertEquals(1, results.getItems().size()); + + // Get bookmarks from data source 2 + param = new CommAccountsSearchParams(0, Account.Type.EMAIL, dataSource2.getId()); + results = commAccountsDAO.getCommAcounts(param, 0, null, false); + assertEquals(1, results.getTotalResultsCount()); + assertEquals(1, results.getItems().size()); + + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex.getMessage()); + } + } public void mimeSearchTest() { // Quick test that everything is initialized From a538dda97907a034a1523638cacf0ba75b687f39 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 12 Nov 2021 13:20:20 -0500 Subject: [PATCH 04/11] Added refresh logic --- .../mainui/datamodel/CommAccountsDAO.java | 118 ++++++------------ 1 file changed, 41 insertions(+), 77 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index c6135f2f0f..5d79880ea5 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -25,6 +25,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; @@ -45,6 +46,8 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; @@ -154,15 +157,17 @@ public class CommAccountsDAO { + "artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, " //NON-NLS + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS + " artifacts.review_status_id AS review_status_id, " //NON-NLS - + " data_artifacts.os_account_obj_id as os_account_obj_id " //NON-NLS + + " data_artifacts.os_account_obj_id as os_account_obj_id, " //NON-NLS + + " attrs.source AS source " + " FROM blackboard_artifacts AS artifacts " + " JOIN blackboard_artifact_types AS types " //NON-NLS + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS + " LEFT JOIN tsk_data_artifacts AS data_artifacts " + " ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id " //NON-NLS - + " JOIN blackboard_attributes ON artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + + " JOIN blackboard_attributes AS attrs " + + " ON artifacts.artifact_id = attrs.artifact_id " //NON-NLS + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS - + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID(); //NON-NLS + + " AND attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID(); //NON-NLS /** * Get all artifacts and their attributes for all communication accounts of the type of interest. @@ -176,7 +181,7 @@ public class CommAccountsDAO { String originalWhereClause = COMMUNICATION_ACCOUNTS_QUERY_STRING - + " AND blackboard_attributes.value_text = '" + type.getTypeName() + "'" //NON-NLS + + " AND attrs.value_text = '" + type.getTypeName() + "'" //NON-NLS + getFilterByDataSourceClause(dataSourceId) + getRejectedArtifactFilterClause(false); // ELTODO @@ -213,22 +218,17 @@ public class CommAccountsDAO { Blackboard blackboard = skCase.getBlackboard(); String pagedWhereClause = getWhereClause(cacheKey); - List list = new ArrayList<>(); - try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(pagedWhereClause); - ResultSet rs = results.getResultSet();) { - List tempList = new ArrayList<>(); - while (rs.next()) { - tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS - } - for (Long artID : tempList) { - list.add(Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getDataArtifactById(artID)); - } + SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(pagedWhereClause); + ResultSet rs = results.getResultSet(); + List fileRows = new ArrayList<>(); + /* + while (rs.next()) { + tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS } - /* - long totalResultsCount = getTotalResultsCount(cacheKey, arts.size()); + long totalResultsCount = getTotalResultsCount(cacheKey, numResults); + - List fileRows = new ArrayList<>(); for (DataArtifact account : arts) { Account blackboardTag = (Account) account; @@ -259,7 +259,8 @@ public class CommAccountsDAO { blackboardTag.getId())); } - return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());* + return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());*/ + return null; } /** @@ -285,68 +286,31 @@ public class CommAccountsDAO { public boolean isRefreshRequired(PropertyChangeEvent evt) { CommAccountsSearchParams params = this.getParameters(); String eventType = evt.getPropertyName(); - /* - // handle artifact/result account changes - if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString()) - || eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) { - - // ignore non-artifact/result account changes - if (params.getTagType() != TagsSearchParams.TagType.RESULT) { - return false; - } - - if (evt instanceof AutopsyEvent) { - if (evt instanceof BlackBoardArtifactTagAddedEvent) { - // An artifact associated with the current case has been tagged. - BlackBoardArtifactTagAddedEvent event = (BlackBoardArtifactTagAddedEvent) evt; - // ensure account added event has a valid content id - if (event.getAddedTag() == null || event.getAddedTag().getContent() == null || event.getAddedTag().getArtifact() == null) { - return false; - } - return params.getTagName().getId() == event.getAddedTag().getId(); - } else if (evt instanceof BlackBoardArtifactTagDeletedEvent) { - // A account has been removed from an artifact associated with the current case. - BlackBoardArtifactTagDeletedEvent event = (BlackBoardArtifactTagDeletedEvent) evt; - // ensure account deleted event has a valid content id - BlackBoardArtifactTagDeletedEvent.DeletedBlackboardArtifactTagInfo deletedTagInfo = event.getDeletedTagInfo(); - if (deletedTagInfo == null) { - return false; - } - return params.getTagName().getId() == deletedTagInfo.getTagID(); + + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * Even with the check above, it is still possible that the + * case will be closed in a different thread before this + * code executes. If that happens, it is possible for the + * event to have a null oldValue. + */ + ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); + if (null != eventData + && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) { + return true; } + } catch (NoCurrentCaseException notUsed) { + // Case is closed, do nothing. } } - - // handle file/content account changes - if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString()) - || eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { - - // ignore non-file/content account changes - if (params.getTagType() != TagsSearchParams.TagType.FILE) { - return false; - } - - if (evt instanceof AutopsyEvent) { - if (evt instanceof ContentTagAddedEvent) { - // Content associated with the current case has been tagged. - ContentTagAddedEvent event = (ContentTagAddedEvent) evt; - // ensure account added event has a valid content id - if (event.getAddedTag() == null || event.getAddedTag().getContent() == null) { - return false; - } - return params.getTagName().getId() == event.getAddedTag().getId(); - } else if (evt instanceof ContentTagDeletedEvent) { - // A account has been removed from content associated with the current case. - ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; - // ensure account deleted event has a valid content id - ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = event.getDeletedTagInfo(); - if (deletedTagInfo == null) { - return false; - } - return params.getTagName().getId() == deletedTagInfo.getTagID(); - } - } - }*/ return false; } } From 27d8a94322330f47f1b1c70974b98f896da3dc1d Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 12 Nov 2021 13:20:36 -0500 Subject: [PATCH 05/11] Added more functional tests --- .../mainui/datamodel/TableSearchTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java index b0cc895714..67c8b872d4 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java @@ -131,10 +131,11 @@ public class TableSearchTest extends NbTestCase { private static final String HOST_COLUMN = "Host"; // Communications accounts test + private static final String ACCOUNT_TYPE_COLUMN = "Account Type"; + private static final String ID_COLUMN = "ID"; private static final String EMAIL_A = "AAA@yahoo.com"; private static final String EMAIL_B = "BBB@gmail.com"; private static final String EMAIL_C = "CCCCC@funmail.com"; - private static final String PHONENUM_1 = "111 777 1111"; private static final String PHONENUM_2 = "222 333 7777"; @@ -654,6 +655,20 @@ public class TableSearchTest extends NbTestCase { assertEquals(1, results.getTotalResultsCount()); assertEquals(1, results.getItems().size()); + // Check that a few of the expected column names are present + List columnDisplayNames = results.getColumns().stream().map(p -> p.getDisplayName()).collect(Collectors.toList()); + assertTrue(columnDisplayNames.contains(ACCOUNT_TYPE_COLUMN)); + assertTrue(columnDisplayNames.contains(ID_COLUMN)); + + // Get the row + RowDTO rowDTO = results.getItems().get(0); + assertTrue(rowDTO instanceof DataArtifactRowDTO); + DataArtifactRowDTO accountResultRowDTO = (DataArtifactRowDTO) rowDTO; + + // Check that some of the expected result tag column values are present + assertTrue(accountResultRowDTO.getCellValues().contains(EMAIL_C)); + assertTrue(accountResultRowDTO.getCellValues().contains(customFile.getName())); + } catch (ExecutionException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); From b0be8046a5e143e3f2ccbbcdcea0e4c963d89303 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 10:36:10 -0500 Subject: [PATCH 06/11] Getting artifacts via new Blackboard API --- .../mainui/datamodel/CommAccountsDAO.java | 72 ++----------------- .../datamodel/CommAccountsSearchParams.java | 12 +--- .../mainui/datamodel/TableSearchTest.java | 6 +- 3 files changed, 9 insertions(+), 81 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index 5d79880ea5..b84694abb0 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -128,69 +128,6 @@ public class CommAccountsDAO { return pagedAcctsStream.collect(Collectors.toList()); } - - /** - * Get the clause that should be used in order to (not) filter out rejected - * results from db queries. - * - * @return A clause that will or will not filter out rejected artifacts - * based on the state of showRejected. - */ - private String getRejectedArtifactFilterClause(boolean showRejected) { - return showRejected ? " " : " AND artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; // - } - - /** - * Returns the clause to filter artifacts by data source. - * - * @return A clause that will or will not filter artifacts by datasource - * based on the CasePreferences groupItemsInTreeByDataSource setting - */ - private String getFilterByDataSourceClause(Long dataSourceId) { - if (dataSourceId != null && dataSourceId > 0) { - return " AND artifacts.data_source_obj_id = " + dataSourceId + " "; - } - return " "; - } - - private final static String COMMUNICATION_ACCOUNTS_QUERY_STRING = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS - + "artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, " //NON-NLS - + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS - + " artifacts.review_status_id AS review_status_id, " //NON-NLS - + " data_artifacts.os_account_obj_id as os_account_obj_id, " //NON-NLS - + " attrs.source AS source " - + " FROM blackboard_artifacts AS artifacts " - + " JOIN blackboard_artifact_types AS types " //NON-NLS - + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS - + " LEFT JOIN tsk_data_artifacts AS data_artifacts " - + " ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id " //NON-NLS - + " JOIN blackboard_attributes AS attrs " - + " ON artifacts.artifact_id = attrs.artifact_id " //NON-NLS - + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS - + " AND attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID(); //NON-NLS - - /** - * Get all artifacts and their attributes for all communication accounts of the type of interest. - * - * @param cacheKey - * @return - */ - String getWhereClause(SearchParams cacheKey) { - Long dataSourceId = cacheKey.getParamData().getDataSourceId(); - Account.Type type = cacheKey.getParamData().getType(); - - String originalWhereClause - = COMMUNICATION_ACCOUNTS_QUERY_STRING - + " AND attrs.value_text = '" + type.getTypeName() + "'" //NON-NLS - + getFilterByDataSourceClause(dataSourceId) - + getRejectedArtifactFilterClause(false); // ELTODO - - String pagedWhereClause = originalWhereClause - + " ORDER BY artifacts.obj_id ASC" - + (cacheKey.getMaxResultsCount() != null && cacheKey.getMaxResultsCount() > 0 ? " LIMIT " + cacheKey.getMaxResultsCount() : "") - + (cacheKey.getStartItem() > 0 ? " OFFSET " + cacheKey.getStartItem() : ""); - return pagedWhereClause; - } long getTotalResultsCount(SearchParams cacheKey, long currentPageSize) throws TskCoreException, NoCurrentCaseException { Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard(); @@ -216,10 +153,11 @@ public class CommAccountsDAO { // get current page of communication accounts results SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); Blackboard blackboard = skCase.getBlackboard(); - String pagedWhereClause = getWhereClause(cacheKey); - - SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(pagedWhereClause); - ResultSet rs = results.getResultSet(); + Account.Type type = cacheKey.getParamData().getType(); + Long dataSourceId = cacheKey.getParamData().getDataSourceId(); + List allArtifacts = blackboard.getArtifacts(BlackboardArtifact.Type.TSK_ACCOUNT, + BlackboardAttribute.Type.TSK_ACCOUNT_TYPE, type.getTypeName(), dataSourceId, + false); // ELTODO List fileRows = new ArrayList<>(); /* while (rs.next()) { diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java index 153f4557ad..91c5e272ae 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsSearchParams.java @@ -27,19 +27,13 @@ import org.sleuthkit.datamodel.Account; public class CommAccountsSearchParams { private final Account.Type type; - private final long account_id; // ELTODO do we need this? private final Long dataSourceId; - public CommAccountsSearchParams(long account_id, Account.Type type, Long dataSourceId) { - this.account_id = account_id; + public CommAccountsSearchParams(Account.Type type, Long dataSourceId) { this.type = type; this.dataSourceId = dataSourceId; } - public long getAccountId() { - return account_id; - } - public Account.Type getType() { return type; } @@ -51,7 +45,6 @@ public class CommAccountsSearchParams { @Override public int hashCode() { int hash = 7; - hash = 23 * hash + Objects.hashCode(this.account_id); hash = 23 * hash + Objects.hashCode(this.type); hash = 23 * hash + Objects.hashCode(this.dataSourceId); return hash; @@ -69,9 +62,6 @@ public class CommAccountsSearchParams { return false; } final CommAccountsSearchParams other = (CommAccountsSearchParams) obj; - if (this.account_id != other.account_id) { - return false; - } if (!Objects.equals(this.dataSourceId, other.dataSourceId)) { return false; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java index 67c8b872d4..a6f38e2274 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java @@ -636,21 +636,21 @@ public class TableSearchTest extends NbTestCase { // Get all contacts CommAccountsDAO commAccountsDAO = MainDAO.getInstance().getCommAccountsDAO(); - CommAccountsSearchParams param = new CommAccountsSearchParams(0, Account.Type.EMAIL, null); + CommAccountsSearchParams param = new CommAccountsSearchParams(Account.Type.EMAIL, null); SearchResultsDTO results = commAccountsDAO.getCommAcounts(param, 0, null, false); //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); assertEquals(3, results.getTotalResultsCount()); assertEquals(3, results.getItems().size()); // Get contacts from data source 2 - param = new CommAccountsSearchParams(0, Account.Type.DEVICE, dataSource1.getId()); + param = new CommAccountsSearchParams(Account.Type.DEVICE, dataSource1.getId()); results = commAccountsDAO.getCommAcounts(param, 0, null, false); //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); assertEquals(1, results.getTotalResultsCount()); assertEquals(1, results.getItems().size()); // Get bookmarks from data source 2 - param = new CommAccountsSearchParams(0, Account.Type.EMAIL, dataSource2.getId()); + param = new CommAccountsSearchParams(Account.Type.EMAIL, dataSource2.getId()); results = commAccountsDAO.getCommAcounts(param, 0, null, false); assertEquals(1, results.getTotalResultsCount()); assertEquals(1, results.getItems().size()); From 2f7ed89bcdcbb628a01700d1760854836e8b4a1a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 12:50:47 -0500 Subject: [PATCH 07/11] CommAccountsDAO uses new Blackboard API and returns paged results --- .../mainui/datamodel/CommAccountsDAO.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index b84694abb0..1561d2722f 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -110,23 +110,23 @@ public class CommAccountsDAO { } /** - * Returns a list of paged communication accounts results. + * Returns a list of paged artifacts. * - * @param accts The communication accounts results. + * @param arts The artifacts. * @param searchParams The search parameters including the paging. * - * @return The list of paged communication accounts results. + * @return The list of paged artifacts. */ - List getPaged(List accts, SearchParams searchParams) { - Stream pagedAcctsStream = accts.stream() - .sorted(Comparator.comparing((acct) -> acct.getAccountID())) + List getPaged(List arts, SearchParams searchParams) { + Stream pagedArtsStream = arts.stream() + .sorted(Comparator.comparing((art) -> art.getId())) .skip(searchParams.getStartItem()); if (searchParams.getMaxResultsCount() != null) { - pagedAcctsStream = pagedAcctsStream.limit(searchParams.getMaxResultsCount()); + pagedArtsStream = pagedArtsStream.limit(searchParams.getMaxResultsCount()); } - return pagedAcctsStream.collect(Collectors.toList()); + return pagedArtsStream.collect(Collectors.toList()); } long getTotalResultsCount(SearchParams cacheKey, long currentPageSize) throws TskCoreException, NoCurrentCaseException { @@ -158,7 +158,19 @@ public class CommAccountsDAO { List allArtifacts = blackboard.getArtifacts(BlackboardArtifact.Type.TSK_ACCOUNT, BlackboardAttribute.Type.TSK_ACCOUNT_TYPE, type.getTypeName(), dataSourceId, false); // ELTODO + + // get current page of artifacts + List pagedArtifacts = getPaged(allArtifacts, cacheKey); + + // Populate the attributes for paged artifacts in the list. This is done using one database call as an efficient way to + // load many artifacts/attributes at once. + blackboard.loadBlackboardAttributes(pagedArtifacts); List fileRows = new ArrayList<>(); + + DataArtifactDAO dataArtDAO = MainDAO.getInstance().getDataArtifactsDAO(); + BlackboardArtifactDAO.TableData tableData = dataArtDAO.createTableData(BlackboardArtifact.Type.TSK_ACCOUNT, pagedArtifacts); + return new DataArtifactTableSearchResultsDTO(BlackboardArtifact.Type.TSK_ACCOUNT, tableData.columnKeys, tableData.rows, cacheKey.getStartItem(), allArtifacts.size()); + /* while (rs.next()) { tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS @@ -198,7 +210,6 @@ public class CommAccountsDAO { } return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());*/ - return null; } /** From 0144996d4d51cdb43b08226719172b69ef12934a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 12:51:06 -0500 Subject: [PATCH 08/11] Functional test bug fixes --- .../sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java index a6f38e2274..f46400cfcc 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java @@ -133,9 +133,9 @@ public class TableSearchTest extends NbTestCase { // Communications accounts test private static final String ACCOUNT_TYPE_COLUMN = "Account Type"; private static final String ID_COLUMN = "ID"; - private static final String EMAIL_A = "AAA@yahoo.com"; - private static final String EMAIL_B = "BBB@gmail.com"; - private static final String EMAIL_C = "CCCCC@funmail.com"; + private static final String EMAIL_A = "aaa@yahoo.com"; + private static final String EMAIL_B = "bbb@gmail.com"; + private static final String EMAIL_C = "ccc@funmail.com"; private static final String PHONENUM_1 = "111 777 1111"; private static final String PHONENUM_2 = "222 333 7777"; From 2764c5a2cefe74442e76f23c946be0a8c29f66fa Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 13:27:41 -0500 Subject: [PATCH 09/11] More tests, code cleanup --- .../mainui/datamodel/CommAccountsDAO.java | 73 +------------------ .../mainui/datamodel/TableSearchTest.java | 41 ++++++++--- 2 files changed, 32 insertions(+), 82 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index 1561d2722f..e7d9eacc3b 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -21,64 +21,38 @@ package org.sleuthkit.autopsy.mainui.datamodel; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.beans.PropertyChangeEvent; -import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; -import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; -import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; -import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.Tag; -import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** * Provides information to populate the results viewer for data in the * Communication Accounts section. */ -@Messages({"CommAccountsDAO.fileColumns.originalName=Source Name", - "CommAccountsDAO.fileColumns.noDescription=No Description"}) +@Messages({"CommAccountsDAO.fileColumns.noDescription=No Description"}) public class CommAccountsDAO { private static final int CACHE_SIZE = Account.Type.PREDEFINED_ACCOUNT_TYPES.size(); // number of cached SearchParams sub-types private static final long CACHE_DURATION = 2; private static final TimeUnit CACHE_DURATION_UNITS = TimeUnit.MINUTES; private final Cache, SearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build(); - - private static final List FILE_TAG_COLUMNS = Arrays.asList( - getFileColumnKey(Bundle.CommAccountsDAO_fileColumns_originalName()) // GVDTODO handle translation - ); private static CommAccountsDAO instance = null; @@ -90,10 +64,6 @@ public class CommAccountsDAO { return instance; } - private static ColumnKey getFileColumnKey(String name) { - return new ColumnKey(name, name, Bundle.CommAccountsDAO_fileColumns_noDescription()); - } - public SearchResultsDTO getCommAcounts(CommAccountsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException { if (key.getType() == null) { throw new IllegalArgumentException("Must have non-null type"); @@ -165,51 +135,10 @@ public class CommAccountsDAO { // Populate the attributes for paged artifacts in the list. This is done using one database call as an efficient way to // load many artifacts/attributes at once. blackboard.loadBlackboardAttributes(pagedArtifacts); - List fileRows = new ArrayList<>(); DataArtifactDAO dataArtDAO = MainDAO.getInstance().getDataArtifactsDAO(); BlackboardArtifactDAO.TableData tableData = dataArtDAO.createTableData(BlackboardArtifact.Type.TSK_ACCOUNT, pagedArtifacts); return new DataArtifactTableSearchResultsDTO(BlackboardArtifact.Type.TSK_ACCOUNT, tableData.columnKeys, tableData.rows, cacheKey.getStartItem(), allArtifacts.size()); - - /* - while (rs.next()) { - tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS - } - - long totalResultsCount = getTotalResultsCount(cacheKey, numResults); - - - for (DataArtifact account : arts) { - Account blackboardTag = (Account) account; - - String name = blackboardTag.getContent().getName(); // As a backup. - try { - name = blackboardTag.getArtifact().getShortDescription(); - } catch (TskCoreException ignore) { - // it's a WARNING, skip - } - - String contentPath; - try { - contentPath = blackboardTag.getContent().getUniquePath(); - } catch (TskCoreException ex) { - contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text"); - } - - List cellValues = Arrays.asList(name, - null, // GVDTODO translation column - contentPath, - blackboardTag.getArtifact().getDisplayName(), - blackboardTag.getComment(), - blackboardTag.getUserName()); - - fileRows.add(new BlackboardArtifactTagsRowDTO( - blackboardTag, - cellValues, - blackboardTag.getId())); - } - - return new BaseSearchResultsDTO(BlackboardArtifactTagsRowDTO.getTypeIdForClass(), Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size());*/ } /** diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java index f46400cfcc..c18fd58e0d 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java @@ -136,8 +136,8 @@ public class TableSearchTest extends NbTestCase { private static final String EMAIL_A = "aaa@yahoo.com"; private static final String EMAIL_B = "bbb@gmail.com"; private static final String EMAIL_C = "ccc@funmail.com"; - private static final String PHONENUM_1 = "111 777 1111"; - private static final String PHONENUM_2 = "222 333 7777"; + private static final String PHONENUM_1 = "1117771111"; + private static final String PHONENUM_2 = "2223337777"; ///////////////////////////////////////////////// // Data to be used across the test methods. @@ -509,9 +509,11 @@ public class TableSearchTest extends NbTestCase { // Add communication accounts openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_A, "Test Module", fileA1); openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_B, "Test Module", fileA2); - AccountFileInstance deviceAccount_2 = openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, "devId1", "Test Module", fileA2); + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, "devId1", "Test Module", fileA2); + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, PHONENUM_1, "Test Module", fileA2); - openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_C, "Test Module", customFile); + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, EMAIL_C, "Test Module", customFile); + openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, PHONENUM_2, "Test Module", customFile); } catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException | OsAccountManager.NotUserSIDException ex) { if (trans != null) { @@ -633,23 +635,21 @@ public class TableSearchTest extends NbTestCase { assertTrue(db != null); try { - // Get all contacts CommAccountsDAO commAccountsDAO = MainDAO.getInstance().getCommAccountsDAO(); + // Get emails from all data sources CommAccountsSearchParams param = new CommAccountsSearchParams(Account.Type.EMAIL, null); SearchResultsDTO results = commAccountsDAO.getCommAcounts(param, 0, null, false); - //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); assertEquals(3, results.getTotalResultsCount()); assertEquals(3, results.getItems().size()); - // Get contacts from data source 2 + // Get device accounts from data source 1 param = new CommAccountsSearchParams(Account.Type.DEVICE, dataSource1.getId()); results = commAccountsDAO.getCommAcounts(param, 0, null, false); - //assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType()); assertEquals(1, results.getTotalResultsCount()); assertEquals(1, results.getItems().size()); - // Get bookmarks from data source 2 + // Get email accounts from data source 2 param = new CommAccountsSearchParams(Account.Type.EMAIL, dataSource2.getId()); results = commAccountsDAO.getCommAcounts(param, 0, null, false); assertEquals(1, results.getTotalResultsCount()); @@ -665,8 +665,29 @@ public class TableSearchTest extends NbTestCase { assertTrue(rowDTO instanceof DataArtifactRowDTO); DataArtifactRowDTO accountResultRowDTO = (DataArtifactRowDTO) rowDTO; - // Check that some of the expected result tag column values are present + // Check that some of the expected result column values are present assertTrue(accountResultRowDTO.getCellValues().contains(EMAIL_C)); + assertTrue(accountResultRowDTO.getCellValues().contains(customFile.getName())); + + // Get phone accounts from all data sources + param = new CommAccountsSearchParams(Account.Type.PHONE, null); + results = commAccountsDAO.getCommAcounts(param, 0, null, false); + assertEquals(2, results.getTotalResultsCount()); + assertEquals(2, results.getItems().size()); + + // Get phone accounts from data source 2 + param = new CommAccountsSearchParams(Account.Type.PHONE, dataSource2.getId()); + results = commAccountsDAO.getCommAcounts(param, 0, null, false); + assertEquals(1, results.getTotalResultsCount()); + assertEquals(1, results.getItems().size()); + + // Get the row + rowDTO = results.getItems().get(0); + assertTrue(rowDTO instanceof DataArtifactRowDTO); + accountResultRowDTO = (DataArtifactRowDTO) rowDTO; + + // Check that some of the expected result column values are present + assertTrue(accountResultRowDTO.getCellValues().contains(PHONENUM_2)); assertTrue(accountResultRowDTO.getCellValues().contains(customFile.getName())); } catch (ExecutionException ex) { From 7d69ddb65f39dc4ada20dfee2ccde2068bb9b36c Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 13:55:23 -0500 Subject: [PATCH 10/11] More fine grained cache invalidation based on account type --- .../autopsy/mainui/datamodel/CommAccountsDAO.java | 14 +++++++++++++- .../autopsy/mainui/datamodel/FileSystemDAO.java | 1 - 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index e7d9eacc3b..0686b6e99e 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -183,10 +183,22 @@ public class CommAccountsDAO { ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) { - return true; + + // check that the update is for the same account type + for (BlackboardArtifact artifact : eventData.getArtifacts()) { + for (BlackboardAttribute atribute : artifact.getAttributes()) { + if (atribute.getAttributeType() == BlackboardAttribute.Type.TSK_ACCOUNT_TYPE) { + if (atribute.getValueString().equals(params.getType().toString())) { + return true; + } + } + } + } } } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. + } catch (TskCoreException ex) { + // There is nothing we can do with the exception. } } return false; diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileSystemDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileSystemDAO.java index 2d59b4c2f8..b3fa860248 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileSystemDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileSystemDAO.java @@ -31,7 +31,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.mainui.datamodel.ContentRowDTO.DirectoryRowDTO; import org.sleuthkit.autopsy.mainui.datamodel.ContentRowDTO.ImageRowDTO; import org.sleuthkit.autopsy.mainui.datamodel.ContentRowDTO.VolumeRowDTO; From 3919e882fd4e1b0068507665ca806322bb6287e6 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 15 Nov 2021 15:35:44 -0500 Subject: [PATCH 11/11] Minor --- .../sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java index 0686b6e99e..720ff4ece6 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/CommAccountsDAO.java @@ -115,7 +115,7 @@ public class CommAccountsDAO { return blackboard.getArtifactsCount(artType.getTypeID()); } } - } + } @NbBundle.Messages({"CommAccounts.name.text=Communication Accounts"}) private SearchResultsDTO fetchCommAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException, SQLException { @@ -127,7 +127,7 @@ public class CommAccountsDAO { Long dataSourceId = cacheKey.getParamData().getDataSourceId(); List allArtifacts = blackboard.getArtifacts(BlackboardArtifact.Type.TSK_ACCOUNT, BlackboardAttribute.Type.TSK_ACCOUNT_TYPE, type.getTypeName(), dataSourceId, - false); // ELTODO + false); // GVDTODO handle approved/rejected account actions // get current page of artifacts List pagedArtifacts = getPaged(allArtifacts, cacheKey);