Merge branch '8148-viewsDaoTree' into 8151-treeChildren

This commit is contained in:
Greg DiCristofaro 2021-11-08 10:13:39 -05:00
commit 397f9fa1c1
5 changed files with 352 additions and 4 deletions

View File

@ -87,6 +87,17 @@ FileSystemColumnUtils.volumeColumns.length=Length in Sectors
FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector
FileTag.name.text=File Tag FileTag.name.text=File Tag
FileTypesByMimeType.name.text=By MIME Type FileTypesByMimeType.name.text=By MIME Type
OsAccounts.name.text=OS Accounts
OsAccountsDAO.createSheet.comment.displayName=C
OsAccountsDAO.createSheet.count.displayName=O
OsAccountsDAO.createSheet.score.displayName=S
OsAccountsDAO.fileColumns.noDescription=No Description
OsAccountsDAO_accountHostNameProperty_displayName=Host
OsAccountsDAO_accountNameProperty_displayName=Name
OsAccountsDAO_accountRealmNameProperty_displayName=Realm Name
OsAccountsDAO_accountScopeNameProperty_displayName=Scope
OsAccountsDAO_createdTimeProperty_displayName=Creation Time
OsAccountsDAO_loginNameProperty_displayName=Login Name
ResultTag.name.text=Result Tag ResultTag.name.text=Result Tag
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
TagsDAO.fileColumns.changeTimeColLbl=Changed Time TagsDAO.fileColumns.changeTimeColLbl=Changed Time

View File

@ -39,6 +39,7 @@ public class MainDAO {
private final ViewsDAO viewsDAO = ViewsDAO.getInstance(); private final ViewsDAO viewsDAO = ViewsDAO.getInstance();
private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance(); private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance();
private final TagsDAO tagsDAO = TagsDAO.getInstance(); private final TagsDAO tagsDAO = TagsDAO.getInstance();
private final OsAccountsDAO accountsDAO = OsAccountsDAO.getInstance();
public DataArtifactDAO getDataArtifactsDAO() { public DataArtifactDAO getDataArtifactsDAO() {
return dataArtifactDAO; return dataArtifactDAO;
@ -59,4 +60,8 @@ public class MainDAO {
public TagsDAO getTagsDAO() { public TagsDAO getTagsDAO() {
return tagsDAO; return tagsDAO;
} }
public OsAccountsDAO getOsAccountsDAO() {
return accountsDAO;
}
} }

View File

@ -0,0 +1,198 @@
/*
* 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.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Provides information to populate the results viewer for data in the OS
* Accounts section.
*/
@Messages({
"OsAccountsDAO_accountNameProperty_displayName=Name",
"OsAccountsDAO_accountRealmNameProperty_displayName=Realm Name",
"OsAccountsDAO_accountHostNameProperty_displayName=Host",
"OsAccountsDAO_accountScopeNameProperty_displayName=Scope",
"OsAccountsDAO_createdTimeProperty_displayName=Creation Time",
"OsAccountsDAO_loginNameProperty_displayName=Login Name",
"OsAccountsDAO.createSheet.score.displayName=S",
"OsAccountsDAO.createSheet.comment.displayName=C",
"OsAccountsDAO.createSheet.count.displayName=O",
"OsAccountsDAO.fileColumns.noDescription=No Description",})
public class OsAccountsDAO {
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 OS_ACCOUNTS_TYPE_ID = "OS_ACCOUNTS";
private static final List<ColumnKey> OS_ACCOUNTS_WITH_SCO_COLUMNS = Arrays.asList(
getFileColumnKey(Bundle.OsAccountsDAO_accountNameProperty_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_createSheet_score_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_createSheet_comment_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_createSheet_count_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_loginNameProperty_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_accountHostNameProperty_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_accountScopeNameProperty_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_accountRealmNameProperty_displayName()),
getFileColumnKey(Bundle.OsAccountsDAO_createdTimeProperty_displayName()));
private static OsAccountsDAO instance = null;
synchronized static OsAccountsDAO getInstance() {
if (instance == null) {
instance = new OsAccountsDAO();
}
return instance;
}
private static ColumnKey getFileColumnKey(String name) {
return new ColumnKey(name, name, Bundle.OsAccountsDAO_fileColumns_noDescription());
}
public SearchResultsDTO getAccounts(OsAccountsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("Search parameters are null");
} else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
throw new IllegalArgumentException("Data source id must be greater than 0 or null");
}
SearchParams<OsAccountsSearchParams> searchParams = new SearchParams<>(key, startItem, maxCount);
if (hardRefresh) {
this.searchParamsCache.invalidate(searchParams);
}
return searchParamsCache.get(searchParams, () -> fetchAccountsDTOs(searchParams));
}
/**
* Returns a list of paged OS Accounts results.
*
* @param accounts The OS Accounts results.
* @param searchParams The search parameters including the paging.
*
* @return The list of paged OS Accounts results.
*/
List<OsAccount> getPaged(List<OsAccount> accounts, SearchParams<?> searchParams) {
Stream<OsAccount> pagedAccountsStream = accounts.stream()
.sorted(Comparator.comparing((acct) -> acct.getId()))
.skip(searchParams.getStartItem());
if (searchParams.getMaxResultsCount() != null) {
pagedAccountsStream = pagedAccountsStream.limit(searchParams.getMaxResultsCount());
}
return pagedAccountsStream.collect(Collectors.toList());
}
@NbBundle.Messages({"OsAccounts.name.text=OS Accounts"})
private SearchResultsDTO fetchAccountsDTOs(SearchParams<OsAccountsSearchParams> cacheKey) throws NoCurrentCaseException, TskCoreException {
Long dataSourceId = cacheKey.getParamData().getDataSourceId();
// get all accounts
List<OsAccount> allAccounts = (dataSourceId != null && dataSourceId > 0)
? Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccountsByDataSourceObjId(dataSourceId)
: Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccounts();
// get current page of accounts results
List<OsAccount> pagedAccounts = getPaged(allAccounts, cacheKey);
List<RowDTO> fileRows = new ArrayList<>();
for (OsAccount account : pagedAccounts) {
Optional<String> optional = account.getLoginName();
Optional<Long> creationTimeValue = account.getCreationTime();
String timeDisplayStr
= creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get()) : "";
List<Object> cellValues = Arrays.asList(
account.getName() != null ? account.getName() : "",
// GVDTODO handle SCO
// GVDTODO only show if (!UserPreferences.getHideSCOColumns())
null,
null,
// GVDTODO only show if central repository enabled
null,
optional.isPresent() ? optional.get() : "",
"",
"",
"", // GVDTODO this is filled by a background GetOsAccountRealmTask task
timeDisplayStr);
fileRows.add(new BaseRowDTO(
cellValues,
OS_ACCOUNTS_TYPE_ID,
account.getId()));
};
return new BaseSearchResultsDTO(OS_ACCOUNTS_TYPE_ID, Bundle.OsAccounts_name_text(), OS_ACCOUNTS_WITH_SCO_COLUMNS, fileRows, 0, allAccounts.size());
}
/**
* Handles fetching and paging of data for accounts.
*/
public static class AccountFetcher extends DAOFetcher<OsAccountsSearchParams> {
/**
* Main constructor.
*
* @param params Parameters to handle fetching of data.
*/
public AccountFetcher(OsAccountsSearchParams params) {
super(params);
}
@Override
public SearchResultsDTO getSearchResults(int pageSize, int pageIdx, boolean hardRefresh) throws ExecutionException {
return MainDAO.getInstance().getOsAccountsDAO().getAccounts(this.getParameters(), pageIdx * pageSize, (long) pageSize, hardRefresh);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.OS_ACCOUNTS_ADDED.toString())
|| eventType.equals(Case.Events.OS_ACCOUNTS_DELETED.toString())) {
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,62 @@
/*
* 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;
/**
* Key for accessing data about OS Accounts from the DAO.
*/
public class OsAccountsSearchParams {
private final Long dataSourceId;
public OsAccountsSearchParams(Long dataSourceId) {
this.dataSourceId = dataSourceId;
}
public Long getDataSourceId() {
return dataSourceId;
}
@Override
public int hashCode() {
int hash = 7;
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 OsAccountsSearchParams other = (OsAccountsSearchParams) obj;
if (!Objects.equals(this.dataSourceId, other.dataSourceId)) {
return false;
}
return true;
}
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import junit.framework.Assert; import junit.framework.Assert;
@ -37,7 +38,6 @@ import org.sleuthkit.autopsy.testutils.CaseUtils;
import org.sleuthkit.autopsy.testutils.TestUtilsException; import org.sleuthkit.autopsy.testutils.TestUtilsException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.Attribute;
import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -47,9 +47,14 @@ import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.HostManager;
import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.Pool; import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountInstance;
import org.sleuthkit.datamodel.OsAccountManager;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
@ -119,6 +124,9 @@ public class TableSearchTest extends NbTestCase {
private static final String PERSON_HOST_NAME1 = "Host for Person A"; private static final String PERSON_HOST_NAME1 = "Host for Person A";
private static final String PERSON_HOST_NAME2 = "Host for Person B"; private static final String PERSON_HOST_NAME2 = "Host for Person B";
// OS Accounts test
private static final String REALM_NAME_COLUMN = "Realm Name";
private static final String HOST_COLUMN = "Host";
///////////////////////////////////////////////// /////////////////////////////////////////////////
// Data to be used across the test methods. // Data to be used across the test methods.
@ -128,6 +136,7 @@ public class TableSearchTest extends NbTestCase {
SleuthkitCase db = null; // The case database SleuthkitCase db = null; // The case database
Blackboard blackboard = null; // The blackboard Blackboard blackboard = null; // The blackboard
TagsManager tagsManager = null;// Tags manager TagsManager tagsManager = null;// Tags manager
OsAccountManager accountMgr = null;
DataSource dataSource1 = null; // A local files data source DataSource dataSource1 = null; // A local files data source
DataSource dataSource2 = null; // A local files data source DataSource dataSource2 = null; // A local files data source
@ -172,6 +181,9 @@ public class TableSearchTest extends NbTestCase {
// Tags test // Tags test
TagName knownTag1 = null; TagName knownTag1 = null;
TagName tag2 = null; TagName tag2 = null;
// OS Accounts test
OsAccount osAccount1 = null;
public static Test suite() { public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(TableSearchTest.class). NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(TableSearchTest.class).
@ -199,6 +211,7 @@ public class TableSearchTest extends NbTestCase {
sizeSearchTest(); sizeSearchTest();
fileSystemTest(); fileSystemTest();
tagsTest(); tagsTest();
OsAccountsTest();
} }
/** /**
@ -212,6 +225,7 @@ public class TableSearchTest extends NbTestCase {
db = openCase.getSleuthkitCase(); db = openCase.getSleuthkitCase();
blackboard = db.getBlackboard(); blackboard = db.getBlackboard();
tagsManager = openCase.getServices().getTagsManager(); tagsManager = openCase.getServices().getTagsManager();
accountMgr = openCase.getSleuthkitCase().getOsAccountManager();
// Add two logical files data sources // Add two logical files data sources
trans = db.beginTransaction(); trans = db.beginTransaction();
@ -467,8 +481,20 @@ public class TableSearchTest extends NbTestCase {
// Tag the custom file in data source 2 // Tag the custom file in data source 2
openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1); openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1);
} catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException ex) { // Add OS Accounts ---------------------
HostManager hostMrg = openCase.getSleuthkitCase().getHostManager();
Host host1 = hostMrg.getHostByDataSource(dataSource1);
OsAccount osAccount2 = accountMgr.newWindowsOsAccount("S-1-5-21-647283-46237-200", null, null, host1, OsAccountRealm.RealmScope.LOCAL);
accountMgr.newOsAccountInstance(osAccount2, dataSource1, OsAccountInstance.OsAccountInstanceType.ACCESSED);
OsAccount osAccount3 = accountMgr.newWindowsOsAccount("S-1-5-21-647283-46237-300", null, null, host1, OsAccountRealm.RealmScope.UNKNOWN);
accountMgr.newOsAccountInstance(osAccount3, dataSource1, OsAccountInstance.OsAccountInstanceType.REFERENCED);
Host host2 = hostMrg.getHostByDataSource(dataSource2);
osAccount1 = accountMgr.newWindowsOsAccount("S-1-5-21-647283-46237-100", null, null, host2, OsAccountRealm.RealmScope.DOMAIN);
accountMgr.newOsAccountInstance(osAccount1, dataSource2, OsAccountInstance.OsAccountInstanceType.LAUNCHED);
} catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException | OsAccountManager.NotUserSIDException ex) {
if (trans != null) { if (trans != null) {
try { try {
trans.rollback(); trans.rollback();
@ -759,7 +785,52 @@ public class TableSearchTest extends NbTestCase {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage()); Assert.fail(ex.getMessage());
} }
} }
public void OsAccountsTest() {
// Quick test that everything is initialized
assertTrue(db != null);
try {
OsAccountsDAO accountsDAO = MainDAO.getInstance().getOsAccountsDAO();
// Get OS Accounts from data source 1
OsAccountsSearchParams param = new OsAccountsSearchParams(dataSource1.getId());
SearchResultsDTO results = accountsDAO.getAccounts(param, 0, null, false);
assertEquals(2, results.getTotalResultsCount());
assertEquals(2, results.getItems().size());
// Get OS Accounts from all data sources
param = new OsAccountsSearchParams(null);
results = accountsDAO.getAccounts(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// Get OS Accounts from data source 1
param = new OsAccountsSearchParams(dataSource2.getId());
results = accountsDAO.getAccounts(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 osAccountRowDTO = (BaseRowDTO) rowDTO;
// Check that the result is for the custom OS Account
Optional<String> addr = osAccount1.getAddr();
assertTrue(osAccountRowDTO.getCellValues().contains(addr.get()));
// Check that a few of the expected OS Account column names are present
List<String> columnDisplayNames = results.getColumns().stream().map(p -> p.getDisplayName()).collect(Collectors.toList());
assertTrue(columnDisplayNames.contains(REALM_NAME_COLUMN));
assertTrue(columnDisplayNames.contains(HOST_COLUMN));
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
}
public void analysisResultSearchTest() { public void analysisResultSearchTest() {
// Quick test that everything is initialized // Quick test that everything is initialized
@ -1131,5 +1202,6 @@ public class TableSearchTest extends NbTestCase {
db = null; db = null;
blackboard = null; blackboard = null;
tagsManager = null; tagsManager = null;
accountMgr = null;
} }
} }