From 9f2e367b84f9f0471ae9545ccd94f74ff7f19d70 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 4 Nov 2021 12:30:09 -0400 Subject: [PATCH 1/6] First cut --- .../autopsy/mainui/datamodel/AccountsDAO.java | 251 ++++++++++++++++++ .../datamodel/AccountsSearchParams.java | 84 ++++++ .../autopsy/mainui/datamodel/MainDAO.java | 7 +- 3 files changed, 341 insertions(+), 1 deletion(-) create mode 100755 Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java create mode 100755 Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java new file mode 100755 index 0000000000..8f3a1a5627 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java @@ -0,0 +1,251 @@ +/* + * 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.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.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; +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 OS + * Accounts section. + */ +@Messages({ + "AccountsDAO_accountNameProperty_name=Name", + "AccountsDAO_accountNameProperty_displayName=Name", + "AccountsDAO_accountNameProperty_desc=Os Account name", + "AccountsDAO_accountRealmNameProperty_name=RealmName", + "AccountsDAO_accountRealmNameProperty_displayName=Realm Name", + "AccountsDAO_accountRealmNameProperty_desc=OS Account Realm Name", + "AccountsDAO_accountHostNameProperty_name=HostName", + "AccountsDAO_accountHostNameProperty_displayName=Host", + "AccountsDAO_accountHostNameProperty_desc=OS Account Host Name", + "AccountsDAO_accountScopeNameProperty_name=ScopeName", + "AccountsDAO_accountScopeNameProperty_displayName=Scope", + "AccountsDAO_accountScopeNameProperty_desc=OS Account Scope Name", + "AccountsDAO_createdTimeProperty_name=creationTime", + "AccountsDAO_createdTimeProperty_displayName=Creation Time", + "AccountsDAO_createdTimeProperty_desc=OS Account Creation Time", + "AccountsDAO_loginNameProperty_name=loginName", + "AccountsDAO_loginNameProperty_displayName=Login Name", + "AccountsDAO_loginNameProperty_desc=OS Account login name", + "AccountsDAO.createSheet.score.name=S", + "AccountsDAO.createSheet.score.displayName=S", + "AccountsDAO.createSheet.count.name=O", + "AccountsDAO.createSheet.count.displayName=O", + "AccountsDAO.createSheet.comment.name=C", + "AccountsDAO.createSheet.comment.displayName=C" +}) +public class AccountsDAO { + + 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, SearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build(); + + private static final String RESULT_TAG_TYPE_ID = "RESULT_TAG"; + + private static final List FILE_TAG_COLUMNS = Arrays.asList( + getFileColumnKey(Bundle.AccountsDAO_fileColumns_nameColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_originalName()), // GVDTODO handle translation + getFileColumnKey(Bundle.AccountsDAO_fileColumns_filePathColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_commentColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_modifiedTimeColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_changeTimeColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_accessTimeColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_createdTimeColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_sizeColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_md5HashColLbl()), + getFileColumnKey(Bundle.AccountsDAO_fileColumns_userNameColLbl())); + + private static AccountsDAO instance = null; + + synchronized static AccountsDAO getInstance() { + if (instance == null) { + instance = new AccountsDAO(); + } + + return instance; + } + + private static ColumnKey getFileColumnKey(String name) { + return new ColumnKey(name, name, Bundle.AccountsDAO_fileColumns_noDescription()); + } + + public SearchResultsDTO getAccounts(AccountsSearchParams 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 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 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 getPaged(List tags, SearchParams searchParams) { + Stream 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 cacheKey) throws NoCurrentCaseException, TskCoreException { + + Long dataSourceId = cacheKey.getParamData().getDataSourceId(); + TagName tagName = cacheKey.getParamData().getTagName(); + + // get all tag results + List allTags = new ArrayList<>(); + List 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 pagedTags = getPaged(allTags, cacheKey); + + List 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 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()); + } + + /** + * Handles fetching and paging of data for accounts. + */ + public static class AccountFetcher extends DAOFetcher { + + /** + * Main constructor. + * + * @param params Parameters to handle fetching of data. + */ + public AccountFetcher(AccountsSearchParams params) { + super(params); + } + + @Override + public SearchResultsDTO getSearchResults(int pageSize, int pageIdx, boolean hardRefresh) throws ExecutionException { + return MainDAO.getInstance().getAccountsDAO().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; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java new file mode 100755 index 0000000000..78628eb495 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.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.OsAccount.OsAccountType; + +/** + * Key for accessing data about OS Accounts from the DAO. + */ +public class AccountsSearchParams { + + private final OsAccountType accountType; + private final long osAccountObjId; // Object ID within the database + private final Long dataSourceId; + + public AccountsSearchParams(long osAccountObjId, OsAccountType type, Long dataSourceId) { + this.osAccountObjId = osAccountObjId; + this.accountType = type; + this.dataSourceId = dataSourceId; + } + + public long getTagName() { + return osAccountObjId; + } + + public OsAccountType getTagType() { + return accountType; + } + + public Long getDataSourceId() { + return dataSourceId; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.osAccountObjId); + hash = 23 * hash + Objects.hashCode(this.accountType); + 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 AccountsSearchParams other = (AccountsSearchParams) obj; + if (this.osAccountObjId != other.osAccountObjId) { + return false; + } + if (!Objects.equals(this.dataSourceId, other.dataSourceId)) { + return false; + } + if (!Objects.equals(this.accountType, other.accountType)) { + 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 f17c5fe52d..28fc9a4b5a 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java @@ -38,6 +38,7 @@ public class MainDAO { private final AnalysisResultDAO analysisResultDAO = AnalysisResultDAO.getInstance(); private final ViewsDAO viewsDAO = ViewsDAO.getInstance(); private final TagsDAO tagsDAO = TagsDAO.getInstance(); + private final AccountsDAO accountsDAO = AccountsDAO.getInstance(); public DataArtifactDAO getDataArtifactsDAO() { return dataArtifactDAO; @@ -53,5 +54,9 @@ public class MainDAO { public TagsDAO getTagsDAO() { return tagsDAO; - } + } + + public AccountsDAO getAccountsDAO() { + return accountsDAO; + } } From b0fd8c5e20c1ff2c63c06d0981cf324e6ebbfc45 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 4 Nov 2021 14:25:05 -0400 Subject: [PATCH 2/6] More work --- .../autopsy/mainui/datamodel/AccountsDAO.java | 128 +++++++++--------- .../datamodel/AccountsSearchParams.java | 21 --- 2 files changed, 67 insertions(+), 82 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java index 8f3a1a5627..dd6cf63d0f 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java @@ -41,6 +41,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; @@ -73,7 +74,8 @@ import org.sleuthkit.datamodel.TskCoreException; "AccountsDAO.createSheet.count.name=O", "AccountsDAO.createSheet.count.displayName=O", "AccountsDAO.createSheet.comment.name=C", - "AccountsDAO.createSheet.comment.displayName=C" + "AccountsDAO.createSheet.comment.displayName=C", + "AccountsDAO.fileColumns.noDescription=No Description", }) public class AccountsDAO { @@ -112,12 +114,10 @@ public class AccountsDAO { } public SearchResultsDTO getAccounts(AccountsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException { - if (key.getTagName() == null) { - throw new IllegalArgumentException("Must have non-null tag name"); + 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"); - } else if (key.getTagType() == null) { - throw new IllegalArgumentException("Must have non-null tag type"); } SearchParams searchParams = new SearchParams<>(key, startItem, maxCount); @@ -125,83 +125,89 @@ public class AccountsDAO { 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 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"); - } + return searchParamsCache.get(searchParams, () -> fetchAccountsDTOs(searchParams)); } /** - * Returns a list of paged tag results. + * Returns a list of paged OS Accounts results. * - * @param tags The tag results. + * @param tags The OS Accounts results. * @param searchParams The search parameters including the paging. * - * @return The list of paged tag results. + * @return The list of paged OS Accounts results. */ - List getPaged(List tags, SearchParams searchParams) { - Stream pagedTagsStream = tags.stream() - .sorted(Comparator.comparing((tag) -> tag.getId())) + List getPaged(List tags, SearchParams searchParams) { + Stream pagedAccountsStream = tags.stream() + .sorted(Comparator.comparing((acct) -> acct.getId())) .skip(searchParams.getStartItem()); if (searchParams.getMaxResultsCount() != null) { - pagedTagsStream = pagedTagsStream.limit(searchParams.getMaxResultsCount()); + pagedAccountsStream = pagedAccountsStream.limit(searchParams.getMaxResultsCount()); } - return pagedTagsStream.collect(Collectors.toList()); + return pagedAccountsStream.collect(Collectors.toList()); } - private SearchResultsDTO fetchResultTags(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { + private SearchResultsDTO fetchAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { Long dataSourceId = cacheKey.getParamData().getDataSourceId(); - TagName tagName = cacheKey.getParamData().getTagName(); - // get all tag results - List allTags = new ArrayList<>(); - List 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 all accounts + List allAccounts = (dataSourceId != null && dataSourceId > 0) + ? Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccountsByDataSourceObjId(dataSourceId) + : Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccounts(); - // get current page of tag results - List pagedTags = getPaged(allTags, cacheKey); + // get current page of accounts results + List pagedAccounts = getPaged(allAccounts, cacheKey); List fileRows = new ArrayList<>(); - for (Tag tag : pagedTags) { - BlackboardArtifactTag blackboardTag = (BlackboardArtifactTag) tag; + for (OsAccount account : pagedAccounts) { + + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_accountNameProperty_name(), + Bundle.OsAccounts_accountNameProperty_displayName(), + Bundle.OsAccounts_accountNameProperty_desc(), + account.getName() != null ? account.getName() : "")); + addSCOColumns(propertiesSet); + Optional optional = account.getLoginName(); + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_loginNameProperty_name(), + Bundle.OsAccounts_loginNameProperty_displayName(), + Bundle.OsAccounts_loginNameProperty_desc(), + optional.isPresent() ? optional.get() : "")); + + // Fill with empty string, fetch on background task. + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_accountHostNameProperty_name(), + Bundle.OsAccounts_accountHostNameProperty_displayName(), + Bundle.OsAccounts_accountHostNameProperty_desc(), + "")); + + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_accountScopeNameProperty_name(), + Bundle.OsAccounts_accountScopeNameProperty_displayName(), + Bundle.OsAccounts_accountScopeNameProperty_desc(), + "")); + + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_accountRealmNameProperty_name(), + Bundle.OsAccounts_accountRealmNameProperty_displayName(), + Bundle.OsAccounts_accountRealmNameProperty_desc(), + "")); + + Optional creationTimeValue = account.getCreationTime(); + String timeDisplayStr + = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get()) : ""; + + propertiesSet.put(new NodeProperty<>( + Bundle.OsAccounts_createdTimeProperty_name(), + Bundle.OsAccounts_createdTimeProperty_displayName(), + Bundle.OsAccounts_createdTimeProperty_desc(), + timeDisplayStr)); + + - 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 @@ -216,7 +222,7 @@ public class AccountsDAO { blackboardTag.getId())); } - return new BaseSearchResultsDTO(RESULT_TAG_TYPE_ID, Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allTags.size()); + return new BaseSearchResultsDTO(RESULT_TAG_TYPE_ID, Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java index 78628eb495..b60563c3aa 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java @@ -26,24 +26,12 @@ import org.sleuthkit.datamodel.OsAccount.OsAccountType; */ public class AccountsSearchParams { - private final OsAccountType accountType; - private final long osAccountObjId; // Object ID within the database private final Long dataSourceId; public AccountsSearchParams(long osAccountObjId, OsAccountType type, Long dataSourceId) { - this.osAccountObjId = osAccountObjId; - this.accountType = type; this.dataSourceId = dataSourceId; } - public long getTagName() { - return osAccountObjId; - } - - public OsAccountType getTagType() { - return accountType; - } - public Long getDataSourceId() { return dataSourceId; } @@ -51,8 +39,6 @@ public class AccountsSearchParams { @Override public int hashCode() { int hash = 7; - hash = 23 * hash + Objects.hashCode(this.osAccountObjId); - hash = 23 * hash + Objects.hashCode(this.accountType); hash = 23 * hash + Objects.hashCode(this.dataSourceId); return hash; } @@ -69,16 +55,9 @@ public class AccountsSearchParams { return false; } final AccountsSearchParams other = (AccountsSearchParams) obj; - if (this.osAccountObjId != other.osAccountObjId) { - return false; - } if (!Objects.equals(this.dataSourceId, other.dataSourceId)) { return false; } - if (!Objects.equals(this.accountType, other.accountType)) { - return false; - } return true; } - } From bf64606efaa5444c6a4004f5bc4fa097251ab3c7 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 4 Nov 2021 15:44:34 -0400 Subject: [PATCH 3/6] First cut at working AccountsDAO --- .../autopsy/mainui/datamodel/AccountsDAO.java | 120 +++++------------- .../mainui/datamodel/Bundle.properties-MERGED | 11 ++ .../autopsy/mainui/datamodel/ViewsDAO.java | 3 - 3 files changed, 41 insertions(+), 93 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java index dd6cf63d0f..2ff123bac3 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java @@ -25,25 +25,17 @@ 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.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.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; 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.OsAccount; -import org.sleuthkit.datamodel.Tag; -import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** @@ -51,30 +43,15 @@ import org.sleuthkit.datamodel.TskCoreException; * Accounts section. */ @Messages({ - "AccountsDAO_accountNameProperty_name=Name", "AccountsDAO_accountNameProperty_displayName=Name", - "AccountsDAO_accountNameProperty_desc=Os Account name", - "AccountsDAO_accountRealmNameProperty_name=RealmName", "AccountsDAO_accountRealmNameProperty_displayName=Realm Name", - "AccountsDAO_accountRealmNameProperty_desc=OS Account Realm Name", - "AccountsDAO_accountHostNameProperty_name=HostName", "AccountsDAO_accountHostNameProperty_displayName=Host", - "AccountsDAO_accountHostNameProperty_desc=OS Account Host Name", - "AccountsDAO_accountScopeNameProperty_name=ScopeName", "AccountsDAO_accountScopeNameProperty_displayName=Scope", - "AccountsDAO_accountScopeNameProperty_desc=OS Account Scope Name", - "AccountsDAO_createdTimeProperty_name=creationTime", "AccountsDAO_createdTimeProperty_displayName=Creation Time", - "AccountsDAO_createdTimeProperty_desc=OS Account Creation Time", - "AccountsDAO_loginNameProperty_name=loginName", "AccountsDAO_loginNameProperty_displayName=Login Name", - "AccountsDAO_loginNameProperty_desc=OS Account login name", - "AccountsDAO.createSheet.score.name=S", "AccountsDAO.createSheet.score.displayName=S", - "AccountsDAO.createSheet.count.name=O", - "AccountsDAO.createSheet.count.displayName=O", - "AccountsDAO.createSheet.comment.name=C", "AccountsDAO.createSheet.comment.displayName=C", + "AccountsDAO.createSheet.count.displayName=O", "AccountsDAO.fileColumns.noDescription=No Description", }) public class AccountsDAO { @@ -84,20 +61,18 @@ public class AccountsDAO { 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 String RESULT_TAG_TYPE_ID = "RESULT_TAG"; + private static final String OS_ACCOUNTS_TYPE_ID = "OS_ACCOUNTS"; - private static final List FILE_TAG_COLUMNS = Arrays.asList( - getFileColumnKey(Bundle.AccountsDAO_fileColumns_nameColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_originalName()), // GVDTODO handle translation - getFileColumnKey(Bundle.AccountsDAO_fileColumns_filePathColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_commentColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_modifiedTimeColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_changeTimeColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_accessTimeColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_createdTimeColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_sizeColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_md5HashColLbl()), - getFileColumnKey(Bundle.AccountsDAO_fileColumns_userNameColLbl())); + private static final List OS_ACCOUNTS_WITH_SCO_COLUMNS = Arrays.asList( + getFileColumnKey(Bundle.AccountsDAO_accountNameProperty_displayName()), + getFileColumnKey(Bundle.AccountsDAO_createSheet_score_displayName()), + getFileColumnKey(Bundle.AccountsDAO_createSheet_comment_displayName()), + getFileColumnKey(Bundle.AccountsDAO_createSheet_count_displayName()), + getFileColumnKey(Bundle.AccountsDAO_loginNameProperty_displayName()), + getFileColumnKey(Bundle.AccountsDAO_accountHostNameProperty_displayName()), + getFileColumnKey(Bundle.AccountsDAO_accountScopeNameProperty_displayName()), + getFileColumnKey(Bundle.AccountsDAO_accountRealmNameProperty_displayName()), + getFileColumnKey(Bundle.AccountsDAO_createdTimeProperty_displayName())); private static AccountsDAO instance = null; @@ -163,66 +138,31 @@ public class AccountsDAO { List fileRows = new ArrayList<>(); for (OsAccount account : pagedAccounts) { - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_accountNameProperty_name(), - Bundle.OsAccounts_accountNameProperty_displayName(), - Bundle.OsAccounts_accountNameProperty_desc(), - account.getName() != null ? account.getName() : "")); - addSCOColumns(propertiesSet); Optional optional = account.getLoginName(); - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_loginNameProperty_name(), - Bundle.OsAccounts_loginNameProperty_displayName(), - Bundle.OsAccounts_loginNameProperty_desc(), - optional.isPresent() ? optional.get() : "")); - - // Fill with empty string, fetch on background task. - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_accountHostNameProperty_name(), - Bundle.OsAccounts_accountHostNameProperty_displayName(), - Bundle.OsAccounts_accountHostNameProperty_desc(), - "")); - - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_accountScopeNameProperty_name(), - Bundle.OsAccounts_accountScopeNameProperty_displayName(), - Bundle.OsAccounts_accountScopeNameProperty_desc(), - "")); - - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_accountRealmNameProperty_name(), - Bundle.OsAccounts_accountRealmNameProperty_displayName(), - Bundle.OsAccounts_accountRealmNameProperty_desc(), - "")); - Optional creationTimeValue = account.getCreationTime(); String timeDisplayStr = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get()) : ""; - - propertiesSet.put(new NodeProperty<>( - Bundle.OsAccounts_createdTimeProperty_name(), - Bundle.OsAccounts_createdTimeProperty_displayName(), - Bundle.OsAccounts_createdTimeProperty_desc(), - timeDisplayStr)); - - - - - - List cellValues = Arrays.asList(name, - null, // GVDTODO translation column - contentPath, - blackboardTag.getArtifact().getDisplayName(), - blackboardTag.getComment(), - blackboardTag.getUserName()); + List 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, - RESULT_TAG_TYPE_ID, - blackboardTag.getId())); - } + OS_ACCOUNTS_TYPE_ID, + account.getId())); + }; - return new BaseSearchResultsDTO(RESULT_TAG_TYPE_ID, Bundle.ResultTag_name_text(), RESULT_TAG_COLUMNS, fileRows, 0, allAccounts.size()); + return new BaseSearchResultsDTO(OS_ACCOUNTS_TYPE_ID, Bundle.ResultTag_name_text(), OS_ACCOUNTS_WITH_SCO_COLUMNS, fileRows, 0, allAccounts.size()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED index 602b270a71..0489a325bd 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED @@ -1,3 +1,14 @@ +AccountsDAO.createSheet.comment.displayName=C +AccountsDAO.createSheet.comment.name=C +AccountsDAO.createSheet.count.displayName=O +AccountsDAO.createSheet.score.displayName=S +AccountsDAO.fileColumns.noDescription=No Description +AccountsDAO_accountHostNameProperty_displayName=Host +AccountsDAO_accountNameProperty_displayName=Name +AccountsDAO_accountRealmNameProperty_displayName=Realm Name +AccountsDAO_accountScopeNameProperty_displayName=Scope +AccountsDAO_createdTimeProperty_displayName=Creation Time +AccountsDAO_loginNameProperty_displayName=Login Name AnalysisResultDAO.columnKeys.conclusion.description=Conclusion AnalysisResultDAO.columnKeys.conclusion.displayName=Conclusion AnalysisResultDAO.columnKeys.conclusion.name=Conclusion diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java index c5466eb7c0..07198d75d2 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java @@ -22,7 +22,6 @@ 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.HashMap; import java.util.List; import java.util.Map; @@ -31,12 +30,10 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; 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 static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree; import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree; -import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.ExtensionMediaType; import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher; From 5d278f92bd4b1a2de9381416a3b5f0bd8dc918c5 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 4 Nov 2021 15:46:59 -0400 Subject: [PATCH 4/6] Cleanup --- .../autopsy/mainui/datamodel/AccountsDAO.java | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java index 2ff123bac3..78c1bfe1a6 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java @@ -30,6 +30,7 @@ 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; @@ -52,20 +53,19 @@ import org.sleuthkit.datamodel.TskCoreException; "AccountsDAO.createSheet.score.displayName=S", "AccountsDAO.createSheet.comment.displayName=C", "AccountsDAO.createSheet.count.displayName=O", - "AccountsDAO.fileColumns.noDescription=No Description", -}) + "AccountsDAO.fileColumns.noDescription=No Description",}) public class AccountsDAO { 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 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 String OS_ACCOUNTS_TYPE_ID = "OS_ACCOUNTS"; - - private static final List OS_ACCOUNTS_WITH_SCO_COLUMNS = Arrays.asList( + + private static final List OS_ACCOUNTS_WITH_SCO_COLUMNS = Arrays.asList( getFileColumnKey(Bundle.AccountsDAO_accountNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_createSheet_score_displayName()), + getFileColumnKey(Bundle.AccountsDAO_createSheet_score_displayName()), getFileColumnKey(Bundle.AccountsDAO_createSheet_comment_displayName()), getFileColumnKey(Bundle.AccountsDAO_createSheet_count_displayName()), getFileColumnKey(Bundle.AccountsDAO_loginNameProperty_displayName()), @@ -87,14 +87,14 @@ public class AccountsDAO { private static ColumnKey getFileColumnKey(String name) { return new ColumnKey(name, name, Bundle.AccountsDAO_fileColumns_noDescription()); } - + public SearchResultsDTO getAccounts(AccountsSearchParams 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 searchParams = new SearchParams<>(key, startItem, maxCount); if (hardRefresh) { this.searchParamsCache.invalidate(searchParams); @@ -102,17 +102,17 @@ public class AccountsDAO { return searchParamsCache.get(searchParams, () -> fetchAccountsDTOs(searchParams)); } - + /** * Returns a list of paged OS Accounts results. * - * @param tags The 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 getPaged(List tags, SearchParams searchParams) { - Stream pagedAccountsStream = tags.stream() + List getPaged(List accounts, SearchParams searchParams) { + Stream pagedAccountsStream = accounts.stream() .sorted(Comparator.comparing((acct) -> acct.getId())) .skip(searchParams.getStartItem()); @@ -123,15 +123,16 @@ public class AccountsDAO { return pagedAccountsStream.collect(Collectors.toList()); } + @NbBundle.Messages({"OsAccounts.name.text=OS Accounts"}) private SearchResultsDTO fetchAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { Long dataSourceId = cacheKey.getParamData().getDataSourceId(); - + // get all accounts List allAccounts = (dataSourceId != null && dataSourceId > 0) ? Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccountsByDataSourceObjId(dataSourceId) : Case.getCurrentCaseThrows().getSleuthkitCase().getOsAccountManager().getOsAccounts(); - + // get current page of accounts results List pagedAccounts = getPaged(allAccounts, cacheKey); @@ -143,18 +144,18 @@ public class AccountsDAO { String timeDisplayStr = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get()) : ""; List 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); + 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, @@ -162,9 +163,9 @@ public class AccountsDAO { account.getId())); }; - return new BaseSearchResultsDTO(OS_ACCOUNTS_TYPE_ID, Bundle.ResultTag_name_text(), OS_ACCOUNTS_WITH_SCO_COLUMNS, fileRows, 0, allAccounts.size()); + 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. */ @@ -188,7 +189,7 @@ public class AccountsDAO { 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())) { + || eventType.equals(Case.Events.OS_ACCOUNTS_DELETED.toString())) { return true; } return false; From 9c78ddec9d867d527f0227f2f49003a3dee99e48 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 5 Nov 2021 09:47:30 -0400 Subject: [PATCH 5/6] Added functional tests --- .../datamodel/AccountsSearchParams.java | 3 +- .../mainui/datamodel/TableSearchTest.java | 80 ++++++++++++++++++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java index b60563c3aa..ce38c4b994 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.mainui.datamodel; import java.util.Objects; -import org.sleuthkit.datamodel.OsAccount.OsAccountType; /** * Key for accessing data about OS Accounts from the DAO. @@ -28,7 +27,7 @@ public class AccountsSearchParams { private final Long dataSourceId; - public AccountsSearchParams(long osAccountObjId, OsAccountType type, Long dataSourceId) { + public AccountsSearchParams(Long dataSourceId) { this.dataSourceId = dataSourceId; } 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 992dcb9158..ec0edc0051 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 @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.concurrent.ExecutionException; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import junit.framework.Assert; @@ -37,7 +38,6 @@ import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.TestUtilsException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; -import org.sleuthkit.datamodel.Attribute; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -47,9 +47,14 @@ import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.HostManager; import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Pool; 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.SleuthkitCase; 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_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. @@ -128,6 +136,7 @@ public class TableSearchTest extends NbTestCase { SleuthkitCase db = null; // The case database Blackboard blackboard = null; // The blackboard TagsManager tagsManager = null;// Tags manager + OsAccountManager accountMgr = null; DataSource dataSource1 = 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 TagName knownTag1 = null; TagName tag2 = null; + + // OS Accounts test + OsAccount osAccount1 = null; public static Test suite() { NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(TableSearchTest.class). @@ -199,6 +211,7 @@ public class TableSearchTest extends NbTestCase { sizeSearchTest(); fileSystemTest(); tagsTest(); + OsAccountsTest(); } /** @@ -212,6 +225,7 @@ public class TableSearchTest extends NbTestCase { db = openCase.getSleuthkitCase(); blackboard = db.getBlackboard(); tagsManager = openCase.getServices().getTagsManager(); + accountMgr = openCase.getSleuthkitCase().getOsAccountManager(); // Add two logical files data sources trans = db.beginTransaction(); @@ -467,8 +481,20 @@ public class TableSearchTest extends NbTestCase { // Tag the custom file in data source 2 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) { try { trans.rollback(); @@ -759,7 +785,52 @@ public class TableSearchTest extends NbTestCase { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } - } + } + + public void OsAccountsTest() { + // Quick test that everything is initialized + assertTrue(db != null); + + try { + AccountsDAO accountsDAO = MainDAO.getInstance().getAccountsDAO(); + + // Get OS Accounts from data source 1 + AccountsSearchParams param = new AccountsSearchParams(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 AccountsSearchParams(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 AccountsSearchParams(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 addr = osAccount1.getAddr(); + assertTrue(osAccountRowDTO.getCellValues().contains(addr.get())); + + // Check that a few of the expected OS Account column names are present + List 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() { // Quick test that everything is initialized @@ -1131,5 +1202,6 @@ public class TableSearchTest extends NbTestCase { db = null; blackboard = null; tagsManager = null; + accountMgr = null; } } From 5341b320ebd9262274b6faad4ddedac363d9d1be Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 5 Nov 2021 14:14:25 -0400 Subject: [PATCH 6/6] Renamed classes to OsAccountsSearchParams and OsAccountsDAO --- .../mainui/datamodel/Bundle.properties-MERGED | 22 +++---- .../autopsy/mainui/datamodel/MainDAO.java | 4 +- .../{AccountsDAO.java => OsAccountsDAO.java} | 60 +++++++++---------- ...arams.java => OsAccountsSearchParams.java} | 6 +- .../mainui/datamodel/TableSearchTest.java | 8 +-- 5 files changed, 50 insertions(+), 50 deletions(-) rename Core/src/org/sleuthkit/autopsy/mainui/datamodel/{AccountsDAO.java => OsAccountsDAO.java} (73%) rename Core/src/org/sleuthkit/autopsy/mainui/datamodel/{AccountsSearchParams.java => OsAccountsSearchParams.java} (90%) diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED index 0489a325bd..681c2f5660 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED @@ -1,14 +1,3 @@ -AccountsDAO.createSheet.comment.displayName=C -AccountsDAO.createSheet.comment.name=C -AccountsDAO.createSheet.count.displayName=O -AccountsDAO.createSheet.score.displayName=S -AccountsDAO.fileColumns.noDescription=No Description -AccountsDAO_accountHostNameProperty_displayName=Host -AccountsDAO_accountNameProperty_displayName=Name -AccountsDAO_accountRealmNameProperty_displayName=Realm Name -AccountsDAO_accountScopeNameProperty_displayName=Scope -AccountsDAO_createdTimeProperty_displayName=Creation Time -AccountsDAO_loginNameProperty_displayName=Login Name AnalysisResultDAO.columnKeys.conclusion.description=Conclusion AnalysisResultDAO.columnKeys.conclusion.displayName=Conclusion AnalysisResultDAO.columnKeys.conclusion.name=Conclusion @@ -92,6 +81,17 @@ FileSystemColumnUtils.volumeColumns.length=Length in Sectors FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector FileTag.name.text=File Tag 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 TagsDAO.fileColumns.accessTimeColLbl=Accessed Time TagsDAO.fileColumns.changeTimeColLbl=Changed Time diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java index de01496f28..81a21ef749 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/MainDAO.java @@ -39,7 +39,7 @@ public class MainDAO { private final ViewsDAO viewsDAO = ViewsDAO.getInstance(); private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance(); private final TagsDAO tagsDAO = TagsDAO.getInstance(); - private final AccountsDAO accountsDAO = AccountsDAO.getInstance(); + private final OsAccountsDAO accountsDAO = OsAccountsDAO.getInstance(); public DataArtifactDAO getDataArtifactsDAO() { return dataArtifactDAO; @@ -61,7 +61,7 @@ public class MainDAO { return tagsDAO; } - public AccountsDAO getAccountsDAO() { + public OsAccountsDAO getOsAccountsDAO() { return accountsDAO; } } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsDAO.java similarity index 73% rename from Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java rename to Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsDAO.java index 78c1bfe1a6..a5d34088fe 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsDAO.java @@ -44,17 +44,17 @@ import org.sleuthkit.datamodel.TskCoreException; * Accounts section. */ @Messages({ - "AccountsDAO_accountNameProperty_displayName=Name", - "AccountsDAO_accountRealmNameProperty_displayName=Realm Name", - "AccountsDAO_accountHostNameProperty_displayName=Host", - "AccountsDAO_accountScopeNameProperty_displayName=Scope", - "AccountsDAO_createdTimeProperty_displayName=Creation Time", - "AccountsDAO_loginNameProperty_displayName=Login Name", - "AccountsDAO.createSheet.score.displayName=S", - "AccountsDAO.createSheet.comment.displayName=C", - "AccountsDAO.createSheet.count.displayName=O", - "AccountsDAO.fileColumns.noDescription=No Description",}) -public class AccountsDAO { + "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; @@ -64,38 +64,38 @@ public class AccountsDAO { private static final String OS_ACCOUNTS_TYPE_ID = "OS_ACCOUNTS"; private static final List OS_ACCOUNTS_WITH_SCO_COLUMNS = Arrays.asList( - getFileColumnKey(Bundle.AccountsDAO_accountNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_createSheet_score_displayName()), - getFileColumnKey(Bundle.AccountsDAO_createSheet_comment_displayName()), - getFileColumnKey(Bundle.AccountsDAO_createSheet_count_displayName()), - getFileColumnKey(Bundle.AccountsDAO_loginNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_accountHostNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_accountScopeNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_accountRealmNameProperty_displayName()), - getFileColumnKey(Bundle.AccountsDAO_createdTimeProperty_displayName())); + 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 AccountsDAO instance = null; + private static OsAccountsDAO instance = null; - synchronized static AccountsDAO getInstance() { + synchronized static OsAccountsDAO getInstance() { if (instance == null) { - instance = new AccountsDAO(); + instance = new OsAccountsDAO(); } return instance; } private static ColumnKey getFileColumnKey(String name) { - return new ColumnKey(name, name, Bundle.AccountsDAO_fileColumns_noDescription()); + return new ColumnKey(name, name, Bundle.OsAccountsDAO_fileColumns_noDescription()); } - public SearchResultsDTO getAccounts(AccountsSearchParams key, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException { + 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 searchParams = new SearchParams<>(key, startItem, maxCount); + SearchParams searchParams = new SearchParams<>(key, startItem, maxCount); if (hardRefresh) { this.searchParamsCache.invalidate(searchParams); } @@ -124,7 +124,7 @@ public class AccountsDAO { } @NbBundle.Messages({"OsAccounts.name.text=OS Accounts"}) - private SearchResultsDTO fetchAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { + private SearchResultsDTO fetchAccountsDTOs(SearchParams cacheKey) throws NoCurrentCaseException, TskCoreException { Long dataSourceId = cacheKey.getParamData().getDataSourceId(); @@ -169,20 +169,20 @@ public class AccountsDAO { /** * Handles fetching and paging of data for accounts. */ - public static class AccountFetcher extends DAOFetcher { + public static class AccountFetcher extends DAOFetcher { /** * Main constructor. * * @param params Parameters to handle fetching of data. */ - public AccountFetcher(AccountsSearchParams params) { + public AccountFetcher(OsAccountsSearchParams params) { super(params); } @Override public SearchResultsDTO getSearchResults(int pageSize, int pageIdx, boolean hardRefresh) throws ExecutionException { - return MainDAO.getInstance().getAccountsDAO().getAccounts(this.getParameters(), pageIdx * pageSize, (long) pageSize, hardRefresh); + return MainDAO.getInstance().getOsAccountsDAO().getAccounts(this.getParameters(), pageIdx * pageSize, (long) pageSize, hardRefresh); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsSearchParams.java similarity index 90% rename from Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java rename to Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsSearchParams.java index ce38c4b994..e21db61826 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AccountsSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/OsAccountsSearchParams.java @@ -23,11 +23,11 @@ import java.util.Objects; /** * Key for accessing data about OS Accounts from the DAO. */ -public class AccountsSearchParams { +public class OsAccountsSearchParams { private final Long dataSourceId; - public AccountsSearchParams(Long dataSourceId) { + public OsAccountsSearchParams(Long dataSourceId) { this.dataSourceId = dataSourceId; } @@ -53,7 +53,7 @@ public class AccountsSearchParams { if (getClass() != obj.getClass()) { return false; } - final AccountsSearchParams other = (AccountsSearchParams) obj; + final OsAccountsSearchParams other = (OsAccountsSearchParams) obj; 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 ec0edc0051..a42d0433f4 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 @@ -792,22 +792,22 @@ public class TableSearchTest extends NbTestCase { assertTrue(db != null); try { - AccountsDAO accountsDAO = MainDAO.getInstance().getAccountsDAO(); + OsAccountsDAO accountsDAO = MainDAO.getInstance().getOsAccountsDAO(); // Get OS Accounts from data source 1 - AccountsSearchParams param = new AccountsSearchParams(dataSource1.getId()); + 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 AccountsSearchParams(null); + 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 AccountsSearchParams(dataSource2.getId()); + param = new OsAccountsSearchParams(dataSource2.getId()); results = accountsDAO.getAccounts(param, 0, null, false); assertEquals(1, results.getTotalResultsCount()); assertEquals(1, results.getItems().size());