diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 8bf29e70ac..a567e6a31a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -20,12 +20,18 @@ 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.NbBundle.Messages; 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; @@ -37,6 +43,8 @@ import org.sleuthkit.datamodel.CommunicationsManager; */ 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; @@ -103,4 +111,34 @@ final class AccountDeviceInstanceNode extends AbstractNode { actions.add(ResetAndPinAccountsAction.getInstance()); 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.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 += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size())); + } + } 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 new file mode 100755 index 0000000000..b66fab6731 --- /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. + */ +final 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. + */ + private static synchronized 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 + */ + static synchronized List getPersonaAccounts(String typeSpecificID) throws ExecutionException { + return getInstance().accountMap.get(typeSpecificID); + } +}