From a5e5a3d5c5fe0580389ef4e104421235fade93a5 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jun 2020 16:07:40 -0400 Subject: [PATCH 1/3] Added persona information to CVT tooltip --- .../AccountDeviceInstanceNode.java | 50 ++++++++-- .../communications/CVTPersonaCache.java | 95 +++++++++++++++++++ 2 files changed, 136 insertions(+), 9 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 8bf29e70ac..96386c0e59 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -20,12 +20,17 @@ package org.sleuthkit.autopsy.communications; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; import javax.swing.Action; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -36,11 +41,15 @@ import org.sleuthkit.datamodel.CommunicationsManager; * Node to represent an Account Device Instance in the CVT */ final class AccountDeviceInstanceNode extends AbstractNode { - + + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNode.class.getName()); + private final AccountDeviceInstanceKey accountDeviceInstanceKey; private final CommunicationsManager commsManager; private final Account account; - + + private static final String TOOLTIP_TEMPLATE = "Contact: %s - Persona: %s"; + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -51,27 +60,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { String iconPath = Utils.getIconFilePath(account.getAccountType()); this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); } - + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - + CommunicationsManager getCommsManager() { return commsManager; } - + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } - + @Override @NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Items"}) protected Sheet createSheet() { @@ -95,7 +104,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return sheet; } - + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); @@ -103,4 +112,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } + + @Override + public String getShortDescription() { + List personaList; + try { + personaList = CVTPersonaCache.getInstance().getPersonaAccounts(getName()); + } catch (ExecutionException ex) { + logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); + return getDisplayName(); + } + + String personaName; + if (!personaList.isEmpty()) { + personaName = personaList.get(0).getPersona().getName(); + if (personaList.size() > 1) { + personaName += String.format("(1 of %d)", personaList.size()); + } + } else { + personaName = "None"; + } + + return String.format(TOOLTIP_TEMPLATE, getDisplayName(), personaName); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java new file mode 100755 index 0000000000..d4d36fa64d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -0,0 +1,95 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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.communications; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * A singleton cache of the PersonaAccount information. The list of + * PersonaAccounts for a given Account typeSpecificID retrieved on first access + * and evicted from the cache after 5 minutes. + */ +class CVTPersonaCache { + + private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); + private final LoadingCache> accountMap; + + private static CVTPersonaCache instance; + + /** + * Cache constructor. + */ + private CVTPersonaCache() { + accountMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build( + new CacheLoader>() { + @Override + public List load(String key) { + List accountList = new ArrayList<>(); + try { + if(CentralRepository.isEnabled()) { + Collection accounts = PersonaAccount.getPersonaAccountsForIdentifierLike(key); + accountList.addAll(accounts); + } + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, String.format("Unable to load Persona information for account: %s", key), ex); + } + return accountList; + } + } + ); + } + + /** + * Returns the singleton instance of the cache. + * + * @return CVTPersonaCache instance. + */ + static CVTPersonaCache getInstance() { + if (instance == null) { + instance = new CVTPersonaCache(); + } + + return instance; + } + + /** + * Returns the list of PersonaAccounts for the given Account typeSpecificId. + * + * @param typeSpecificID Account typeSpecificId. + * + * @return List of PersonaAccounts for id or empty list if none were found. + * + * @throws ExecutionException + */ + List getPersonaAccounts(String typeSpecificID) throws ExecutionException { + return accountMap.get(typeSpecificID); + } +} From 90c74d2aaa32e77de374fa13df072c3ac9285d0e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 10:14:56 -0400 Subject: [PATCH 2/3] Fixed codacy issues and handled review comment --- .../communications/AccountDeviceInstanceNode.java | 14 +++++++++++--- .../autopsy/communications/CVTPersonaCache.java | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 96386c0e59..c977cb81fc 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -28,6 +28,7 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; import org.sleuthkit.autopsy.coreutils.Logger; @@ -113,11 +114,18 @@ final class AccountDeviceInstanceNode extends AbstractNode { return actions.toArray(new Action[actions.size()]); } + @Messages({ + "# {0} - Contact Name", + "# {1} - Persona Name", + "AccountInstanceNode_Tooltip_Template=Contact: {0} - Persona: {1}", + "# {0} - PersonaAccount count", + "AccountInstanceNode_Tooltip_suffix=(1 of {0})" + }) @Override public String getShortDescription() { List personaList; try { - personaList = CVTPersonaCache.getInstance().getPersonaAccounts(getName()); + personaList = CVTPersonaCache.getPersonaAccounts(getName()); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); @@ -127,12 +135,12 @@ final class AccountDeviceInstanceNode extends AbstractNode { if (!personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); if (personaList.size() > 1) { - personaName += String.format("(1 of %d)", personaList.size()); + personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size())); } } else { personaName = "None"; } - return String.format(TOOLTIP_TEMPLATE, getDisplayName(), personaName); + return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index d4d36fa64d..f4bface5fb 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -37,7 +37,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * PersonaAccounts for a given Account typeSpecificID retrieved on first access * and evicted from the cache after 5 minutes. */ -class CVTPersonaCache { +final class CVTPersonaCache { private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); private final LoadingCache> accountMap; @@ -72,7 +72,7 @@ class CVTPersonaCache { * * @return CVTPersonaCache instance. */ - static CVTPersonaCache getInstance() { + private static CVTPersonaCache getInstance() { if (instance == null) { instance = new CVTPersonaCache(); } @@ -89,7 +89,7 @@ class CVTPersonaCache { * * @throws ExecutionException */ - List getPersonaAccounts(String typeSpecificID) throws ExecutionException { - return accountMap.get(typeSpecificID); + static synchronized List getPersonaAccounts(String typeSpecificID) throws ExecutionException { + return getInstance().accountMap.get(typeSpecificID); } } From a3bf0d8de24693711efa4f18964697dc9d8a0b26 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 13:41:00 -0400 Subject: [PATCH 3/3] Fixed codacy issue --- .../AccountDeviceInstanceNode.java | 28 +++++++++---------- .../communications/CVTPersonaCache.java | 10 +++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index c977cb81fc..a567e6a31a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -42,15 +42,13 @@ import org.sleuthkit.datamodel.CommunicationsManager; * Node to represent an Account Device Instance in the CVT */ final class AccountDeviceInstanceNode extends AbstractNode { - + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNode.class.getName()); - + private final AccountDeviceInstanceKey accountDeviceInstanceKey; private final CommunicationsManager commsManager; private final Account account; - - private static final String TOOLTIP_TEMPLATE = "Contact: %s - Persona: %s"; - + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -61,27 +59,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { String iconPath = Utils.getIconFilePath(account.getAccountType()); this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); } - + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - + CommunicationsManager getCommsManager() { return commsManager; } - + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } - + @Override @NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Items"}) protected Sheet createSheet() { @@ -105,7 +103,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return sheet; } - + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); @@ -113,7 +111,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } - + @Messages({ "# {0} - Contact Name", "# {1} - Persona Name", @@ -130,7 +128,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); } - + String personaName; if (!personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); @@ -140,7 +138,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { } else { personaName = "None"; } - + return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index f4bface5fb..b66fab6731 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -38,10 +38,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; * and evicted from the cache after 5 minutes. */ final class CVTPersonaCache { - + private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); private final LoadingCache> accountMap; - + private static CVTPersonaCache instance; /** @@ -54,7 +54,7 @@ final class CVTPersonaCache { public List load(String key) { List accountList = new ArrayList<>(); try { - if(CentralRepository.isEnabled()) { + if (CentralRepository.isEnabled()) { Collection accounts = PersonaAccount.getPersonaAccountsForIdentifierLike(key); accountList.addAll(accounts); } @@ -72,11 +72,11 @@ final class CVTPersonaCache { * * @return CVTPersonaCache instance. */ - private static CVTPersonaCache getInstance() { + private static synchronized CVTPersonaCache getInstance() { if (instance == null) { instance = new CVTPersonaCache(); } - + return instance; }