Merge pull request #7394 from eugene7646/tags_dao_8123

Tags DAO (8123)
This commit is contained in:
Ann Priestman 2021-11-02 13:52:04 -04:00 committed by GitHub
commit 78f827e77c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 606 additions and 2 deletions

View File

@ -46,7 +46,26 @@ FileExtRootFilter_documents_displayName=Documents
FileExtRootFilter_executable_displayName=Executable
FileExtRootFilter_image_displayName=Images
FileExtRootFilter_video_displayName=Video
FileTag.name.text=File Tag
FileTypesByMimeType.name.text=By MIME Type
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
TagsDAO.fileColumns.changeTimeColLbl=Changed Time
TagsDAO.fileColumns.commentColLbl=Comment
TagsDAO.fileColumns.createdTimeColLbl=Created Time
TagsDAO.fileColumns.filePathColLbl=File Path
TagsDAO.fileColumns.md5HashColLbl=MD5 Hash
TagsDAO.fileColumns.modifiedTimeColLbl=Modified Time
TagsDAO.fileColumns.nameColLbl=Name
TagsDAO.fileColumns.noDescription=No Description
TagsDAO.fileColumns.originalName=Original Name
TagsDAO.fileColumns.sizeColLbl=Size
TagsDAO.fileColumns.userNameColLbl=User Name
TagsDAO.tagColumns.commentColLbl=Comment
TagsDAO.tagColumns.origNameColLbl=Original Name
TagsDAO.tagColumns.sourceNameColLbl=Source Name
TagsDAO.tagColumns.sourcePathColLbl=Source File Path
TagsDAO.tagColumns.typeColLbl=Result Type
TagsDAO.tagColumns.userNameColLbl=User Name
ThreePanelViewsDAO.fileColumns.accessTimeColLbl=Access Time
ThreePanelViewsDAO.fileColumns.attrAddrColLbl=Attr. Addr.
ThreePanelViewsDAO.fileColumns.changeTimeColLbl=Change Time

View File

@ -37,6 +37,7 @@ public class MainDAO {
private final DataArtifactDAO dataArtifactDAO = DataArtifactDAO.getInstance();
private final AnalysisResultDAO analysisResultDAO = AnalysisResultDAO.getInstance();
private final ViewsDAO viewsDAO = ViewsDAO.getInstance();
private final TagsDAO tagsDAO = TagsDAO.getInstance();
public DataArtifactDAO getDataArtifactsDAO() {
return dataArtifactDAO;
@ -49,4 +50,8 @@ public class MainDAO {
public ViewsDAO getViewsDAO() {
return viewsDAO;
}
public TagsDAO getTagsDAO() {
return tagsDAO;
}
}

View File

@ -0,0 +1,371 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.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.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.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
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 allTags
section.
*/
@Messages({"TagsDAO.fileColumns.nameColLbl=Name",
"TagsDAO.fileColumns.originalName=Original Name",
"TagsDAO.fileColumns.filePathColLbl=File Path",
"TagsDAO.fileColumns.commentColLbl=Comment",
"TagsDAO.fileColumns.modifiedTimeColLbl=Modified Time",
"TagsDAO.fileColumns.changeTimeColLbl=Changed Time",
"TagsDAO.fileColumns.accessTimeColLbl=Accessed Time",
"TagsDAO.fileColumns.createdTimeColLbl=Created Time",
"TagsDAO.fileColumns.sizeColLbl=Size",
"TagsDAO.fileColumns.md5HashColLbl=MD5 Hash",
"TagsDAO.fileColumns.userNameColLbl=User Name",
"TagsDAO.fileColumns.noDescription=No Description",
"TagsDAO.tagColumns.sourceNameColLbl=Source Name",
"TagsDAO.tagColumns.origNameColLbl=Original Name",
"TagsDAO.tagColumns.sourcePathColLbl=Source File Path",
"TagsDAO.tagColumns.typeColLbl=Result Type",
"TagsDAO.tagColumns.commentColLbl=Comment",
"TagsDAO.tagColumns.userNameColLbl=User Name"})
public class TagsDAO {
private static final int CACHE_SIZE = 5; // rule of thumb: 5 entries times 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<SearchParams<?>, SearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build();
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private static final String FILE_TAG_TYPE_ID = "FILE_TAG";
private static final String RESULT_TAG_TYPE_ID = "RESULT_TAG";
private static final List<ColumnKey> FILE_TAG_COLUMNS = Arrays.asList(
getFileColumnKey(Bundle.TagsDAO_fileColumns_nameColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_originalName()), // GVDTODO handle translation
getFileColumnKey(Bundle.TagsDAO_fileColumns_filePathColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_commentColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_modifiedTimeColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_changeTimeColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_accessTimeColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_createdTimeColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_sizeColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_md5HashColLbl()),
getFileColumnKey(Bundle.TagsDAO_fileColumns_userNameColLbl()));
private static final List<ColumnKey> RESULT_TAG_COLUMNS = Arrays.asList(
getFileColumnKey(Bundle.TagsDAO_tagColumns_sourceNameColLbl()),
getFileColumnKey(Bundle.TagsDAO_tagColumns_origNameColLbl()),
getFileColumnKey(Bundle.TagsDAO_tagColumns_sourcePathColLbl()),
getFileColumnKey(Bundle.TagsDAO_tagColumns_typeColLbl()),
getFileColumnKey(Bundle.TagsDAO_tagColumns_commentColLbl()),
getFileColumnKey(Bundle.TagsDAO_tagColumns_userNameColLbl()));
private static TagsDAO instance = null;
synchronized static TagsDAO getInstance() {
if (instance == null) {
instance = new TagsDAO();
}
return instance;
}
private static ColumnKey getFileColumnKey(String name) {
return new ColumnKey(name, name, Bundle.TagsDAO_fileColumns_noDescription());
}
public SearchResultsDTO getTags(TagsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
if (key.getTagName() == null) {
throw new IllegalArgumentException("Must have non-null tag name");
} else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
throw new IllegalArgumentException("Data source id must be greater than 0 or null");
} else if (key.getTagType() == null) {
throw new IllegalArgumentException("Must have non-null tag type");
}
SearchParams<TagsSearchParams> searchParams = new SearchParams<>(key, startItem, maxCount);
if (hardRefresh) {
this.searchParamsCache.invalidate(searchParams);
}
return searchParamsCache.get(searchParams, () -> fetchTagsDTOs(searchParams));
}
@NbBundle.Messages({"FileTag.name.text=File Tag",
"ResultTag.name.text=Result Tag"})
private SearchResultsDTO fetchTagsDTOs(SearchParams<TagsSearchParams> cacheKey) throws NoCurrentCaseException, TskCoreException {
switch (cacheKey.getParamData().getTagType()) {
case FILE:
return fetchFileTags(cacheKey);
case RESULT:
return fetchResultTags(cacheKey);
default:
throw new IllegalArgumentException("Unsupported tag type");
}
}
/**
* Returns a list of paged tag results.
*
* @param tags The tag results.
* @param searchParams The search parameters including the paging.
*
* @return The list of paged tag results.
*/
List<? extends Tag> getPaged(List<? extends Tag> tags, SearchParams<?> searchParams) {
Stream<? extends Tag> pagedTagsStream = tags.stream()
.sorted(Comparator.comparing((tag) -> tag.getId()))
.skip(searchParams.getStartItem());
if (searchParams.getMaxResultsCount() != null) {
pagedTagsStream = pagedTagsStream.limit(searchParams.getMaxResultsCount());
}
return pagedTagsStream.collect(Collectors.toList());
}
private SearchResultsDTO fetchResultTags(SearchParams<TagsSearchParams> cacheKey) throws NoCurrentCaseException, TskCoreException {
Long dataSourceId = cacheKey.getParamData().getDataSourceId();
TagName tagName = cacheKey.getParamData().getTagName();
// get all tag results
List<BlackboardArtifactTag> allTags = new ArrayList<>();
List<BlackboardArtifactTag> artifactTags = (dataSourceId != null && dataSourceId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, dataSourceId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
allTags.add(tag);
}
}
} else {
allTags.addAll(artifactTags);
}
// get current page of tag results
List<? extends Tag> pagedTags = getPaged(allTags, cacheKey);
List<RowDTO> fileRows = new ArrayList<>();
for (Tag tag : pagedTags) {
BlackboardArtifactTag blackboardTag = (BlackboardArtifactTag) tag;
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<Object> cellValues = Arrays.asList(name,
null, // GVDTODO translation column
contentPath,
blackboardTag.getArtifact().getDisplayName(),
blackboardTag.getComment(),
blackboardTag.getUserName());
fileRows.add(new BaseRowDTO(
cellValues,
RESULT_TAG_TYPE_ID,
blackboardTag.getId()));
}
return new BaseSearchResultsDTO(RESULT_TAG_TYPE_ID, Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allTags.size());
}
private SearchResultsDTO fetchFileTags(SearchParams<TagsSearchParams> cacheKey) throws NoCurrentCaseException, TskCoreException {
Long dataSourceId = cacheKey.getParamData().getDataSourceId();
TagName tagName = cacheKey.getParamData().getTagName();
// get all tag results
List<ContentTag> allTags = new ArrayList<>();
List<ContentTag> contentTags = (dataSourceId != null && dataSourceId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, dataSourceId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
allTags.add(tag);
}
}
} else {
allTags.addAll(contentTags);
}
// get current page of tag results
List<? extends Tag> pagedTags = getPaged(allTags, cacheKey);
List<RowDTO> fileRows = new ArrayList<>();
for (Tag tag : pagedTags) {
ContentTag contentTag = (ContentTag) tag;
Content content = contentTag.getContent();
String contentPath = content.getUniquePath();
AbstractFile file = content instanceof AbstractFile ? (AbstractFile) content : null;
List<Object> cellValues = Arrays.asList(
content.getName(),
null, // GVDTODO translation column
contentPath,
contentTag.getComment(),
file != null ? TimeZoneUtils.getFormattedTime(file.getMtime()) : "",
file != null ? TimeZoneUtils.getFormattedTime(file.getCtime()) : "",
file != null ? TimeZoneUtils.getFormattedTime(file.getAtime()) : "",
file != null ? TimeZoneUtils.getFormattedTime(file.getCrtime()) : "",
content.getSize(),
file != null ? StringUtils.defaultString(file.getMd5Hash()) : "",
contentTag.getUserName());
fileRows.add(new BaseRowDTO(
cellValues,
FILE_TAG_TYPE_ID,
file.getId()));
}
return new BaseSearchResultsDTO(FILE_TAG_TYPE_ID, Bundle.FileTag_name_text(), FILE_TAG_COLUMNS, fileRows, 0, allTags.size());
}
/**
* Handles fetching and paging of data for allTags.
*/
public static class TagFetcher extends DAOFetcher<TagsSearchParams> {
/**
* Main constructor.
*
* @param params Parameters to handle fetching of data.
*/
public TagFetcher(TagsSearchParams params) {
super(params);
}
@Override
public SearchResultsDTO getSearchResults(int pageSize, int pageIdx, boolean hardRefresh) throws ExecutionException {
return MainDAO.getInstance().getTagsDAO().getTags(this.getParameters(), pageIdx * pageSize, (long) pageSize, hardRefresh);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
TagsSearchParams params = this.getParameters();
String eventType = evt.getPropertyName();
// handle artifact/result tag changes
if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())
|| eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
// ignore non-artifact/result tag 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 tag 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 tag has been removed from an artifact associated with the current case.
BlackBoardArtifactTagDeletedEvent event = (BlackBoardArtifactTagDeletedEvent) evt;
// ensure tag 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 tag changes
if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())
|| eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
// ignore non-file/content tag 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 tag 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 tag has been removed from content associated with the current case.
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
// ensure tag 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;
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.TagName;
/**
* Key for accessing data about tags from the DAO.
*/
public class TagsSearchParams {
public enum TagType {
FILE,
RESULT;
}
private final TagType type;
private final TagName tagName;
private final Long dataSourceId;
public TagsSearchParams(TagName tagName, TagType type, Long dataSourceId) {
this.tagName = tagName;
this.type = type;
this.dataSourceId = dataSourceId;
}
public TagName getTagName() {
return tagName;
}
public TagType getTagType() {
return type;
}
public Long getDataSourceId() {
return dataSourceId;
}
@Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + Objects.hashCode(this.tagName);
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 TagsSearchParams other = (TagsSearchParams) obj;
if (!Objects.equals(this.tagName, other.tagName)) {
return false;
}
if (!Objects.equals(this.dataSourceId, other.dataSourceId)) {
return false;
}
if (!Objects.equals(this.type, other.type)) {
return false;
}
return true;
}
}

View File

@ -32,6 +32,7 @@ import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.testutils.CaseUtils;
import org.sleuthkit.autopsy.testutils.TestUtilsException;
import org.sleuthkit.datamodel.AbstractFile;
@ -45,6 +46,7 @@ import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskCoreException;
@ -87,12 +89,22 @@ public class TableSearchTest extends NbTestCase {
private static final String KEYWORD_PREVIEW = "There is a bomb.";
// Extension and MIME type test
private static AbstractFile customFile;
private static final String CUSTOM_MIME_TYPE = "fake/type";
private static final String CUSTOM_MIME_TYPE_FILE_NAME = "test.fake";
private static final String CUSTOM_EXTENSION = "fake";
private static final Set<String> CUSTOM_EXTENSIONS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("." + CUSTOM_EXTENSION))); //NON-NLS
private static final Set<String> EMPTY_RESULT_SET_EXTENSIONS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(".blah", ".blah2", ".crazy"))); //NON-NLS
// Tag test
private static final String TAG_COMMENT = "Tag comment";
private static final String TAG_DESCRIPTION = "Tag description";
private static final String MD5_COLUMN = "MD5 Hash";
private static final String FILE_PATH_COLUMN = "File Path";
private static final String MODIFIED_TIME_COLUMN = "Modified Time";
private static final String SOURCE_NAME_COLUMN = "Source Name";
private static final String SOURCE_FILE_PATH_COLUMN = "Source File Path";
/////////////////////////////////////////////////
// Data to be used across the test methods.
// These are initialized in setUpCaseDatabase().
@ -100,6 +112,7 @@ public class TableSearchTest extends NbTestCase {
Case openCase = null; // The case for testing
SleuthkitCase db = null; // The case database
Blackboard blackboard = null; // The blackboard
TagsManager tagsManager = null;// Tags manager
DataSource dataSource1 = null; // A local files data source
DataSource dataSource2 = null; // A local files data source
@ -126,6 +139,10 @@ public class TableSearchTest extends NbTestCase {
AnalysisResult keywordHitAnalysisResult = null; // A keyword hit
Content keywordHitSource = null; // The source of the keyword hit above
// Tags test
TagName knownTag1 = null;
TagName tag2 = null;
public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(TableSearchTest.class).
clusters(".*").
@ -150,6 +167,7 @@ public class TableSearchTest extends NbTestCase {
mimeSearchTest();
extensionSearchTest();
sizeSearchTest();
tagsTest();
}
/**
@ -161,6 +179,7 @@ public class TableSearchTest extends NbTestCase {
openCase = CaseUtils.createAsCurrentCase("testTableSearchCase");
db = openCase.getSleuthkitCase();
blackboard = db.getBlackboard();
tagsManager = openCase.getServices().getTagsManager();
// Add two logical files data sources
SleuthkitCase.CaseDbTransaction trans = db.beginTransaction();
@ -191,7 +210,7 @@ public class TableSearchTest extends NbTestCase {
fileB1.setMIMEType("text/plain");
fileB1.save();
AbstractFile customFile = db.addLocalFile(CUSTOM_MIME_TYPE_FILE_NAME, "", 67000000, 0, 0, 0, 0, true, TskData.EncodingType.NONE, folderB1);
customFile = db.addLocalFile(CUSTOM_MIME_TYPE_FILE_NAME, "", 67000000, 0, 0, 0, 0, true, TskData.EncodingType.NONE, folderB1);
customFile.setMIMEType(CUSTOM_MIME_TYPE);
customFile.save();
@ -322,7 +341,25 @@ public class TableSearchTest extends NbTestCase {
null, KEYWORD_SET_1, null, attrs).getAnalysisResult();
keywordHitSource = hashHitAnalysisResult;
} catch (TestUtilsException | TskCoreException | BlackboardException ex) {
// Add tags ----
knownTag1 = tagsManager.addTagName("Tag 1", TAG_DESCRIPTION, TagName.HTML_COLOR.RED, TskData.FileKnown.KNOWN);
tag2 = tagsManager.addTagName("Tag 2", "Descrition");
// Tag the custom artifacts in data source 1
openCase.getServices().getTagsManager().addBlackboardArtifactTag(customDataArtifact, knownTag1, TAG_COMMENT);
openCase.getServices().getTagsManager().addBlackboardArtifactTag(customAnalysisResult, tag2, "Comment 2");
// Tag file in data source 1
openCase.getServices().getTagsManager().addContentTag(fileA2, tag2);
openCase.getServices().getTagsManager().addContentTag(fileA3, tag2);
// Tag file in data source 2
openCase.getServices().getTagsManager().addContentTag(fileB1, tag2);
// Tag the custom file in data source 2
openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1);
} catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
@ -527,6 +564,87 @@ public class TableSearchTest extends NbTestCase {
}
}
public void tagsTest() {
// Quick test that everything is initialized
assertTrue(db != null);
try {
TagsDAO tagsDAO = MainDAO.getInstance().getTagsDAO();
// Get "Tag1" file tags from data source 1
TagsSearchParams param = new TagsSearchParams(knownTag1, TagsSearchParams.TagType.FILE, dataSource1.getId());
SearchResultsDTO results = tagsDAO.getTags(param, 0, null, false);
assertEquals(0, results.getTotalResultsCount());
assertEquals(0, results.getItems().size());
// Get "Tag2" file tags from data source 1
param = new TagsSearchParams(tag2, TagsSearchParams.TagType.FILE, dataSource1.getId());
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(2, results.getTotalResultsCount());
assertEquals(2, results.getItems().size());
// Get "Tag2" file tags from all data sources
param = new TagsSearchParams(tag2, TagsSearchParams.TagType.FILE, null);
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// Get "Tag1" file tags from data source 2
param = new TagsSearchParams(knownTag1, TagsSearchParams.TagType.FILE, dataSource2.getId());
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(1, results.getTotalResultsCount());
assertEquals(1, results.getItems().size());
// Get the row
RowDTO rowDTO = results.getItems().get(0);
assertTrue(rowDTO instanceof BaseRowDTO);
BaseRowDTO tagResultRowDTO = (BaseRowDTO) rowDTO;
// Check that the file tag is for the custom file
assertTrue(tagResultRowDTO.getCellValues().contains(customFile.getName()));
// Check that a few of the expected file tag column names are present
List<String> columnDisplayNames = results.getColumns().stream().map(p -> p.getDisplayName()).collect(Collectors.toList());
assertTrue(columnDisplayNames.contains(MD5_COLUMN));
assertTrue(columnDisplayNames.contains(FILE_PATH_COLUMN));
assertTrue(columnDisplayNames.contains(MODIFIED_TIME_COLUMN));
// Check that the result tag columns are not present
assertFalse(columnDisplayNames.contains(SOURCE_NAME_COLUMN));
assertFalse(columnDisplayNames.contains(SOURCE_FILE_PATH_COLUMN));
// Get "Tag1" result tags from data source 2
param = new TagsSearchParams(knownTag1, TagsSearchParams.TagType.RESULT, dataSource2.getId());
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(0, results.getTotalResultsCount());
assertEquals(0, results.getItems().size());
// Get "Tag2" result tags from data source 1
param = new TagsSearchParams(tag2, TagsSearchParams.TagType.RESULT, dataSource1.getId());
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(1, results.getTotalResultsCount());
assertEquals(1, results.getItems().size());
// Get "Tag1" result tags from data source 1
param = new TagsSearchParams(knownTag1, TagsSearchParams.TagType.RESULT, dataSource1.getId());
results = tagsDAO.getTags(param, 0, null, false);
assertEquals(1, results.getTotalResultsCount());
assertEquals(1, results.getItems().size());
// Get the row
rowDTO = results.getItems().get(0);
assertTrue(rowDTO instanceof BaseRowDTO);
tagResultRowDTO = (BaseRowDTO) rowDTO;
// Check that some of the expected result tag column values are present
assertTrue(tagResultRowDTO.getCellValues().contains(TAG_COMMENT));
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
}
public void analysisResultSearchTest() {
// Quick test that everything is initialized
assertTrue(db != null);
@ -807,5 +925,7 @@ public class TableSearchTest extends NbTestCase {
}
openCase = null;
db = null;
blackboard = null;
tagsManager = null;
}
}