Merge pull request #6545 from kellykelly3/7078-cvt-tooltips

7078 Added contact and person information to callog and message viewer too…
This commit is contained in:
Richard Cordovano 2021-01-04 13:41:55 -05:00 committed by GitHub
commit 7891b70643
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 262 additions and 77 deletions

View File

@ -20,27 +20,21 @@ 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.communications.relationships.RelationshipsNodeUtilities;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Node to represent an Account Device Instance in the CVT
@ -118,48 +112,8 @@ 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<PersonaAccount> personaList;
List<BlackboardArtifact> contactArtifactList;
try {
personaList = CVTPersonaCache.getPersonaAccounts(account);
contactArtifactList = ContactCache.getContacts(account);
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
return getDisplayName();
}
String personaName;
if (personaList != null && !personaList.isEmpty()) {
personaName = personaList.get(0).getPersona().getName();
if (personaList.size() > 1) {
personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size()));
}
} else {
personaName = "None";
}
String contactName = getDisplayName();
if (contactArtifactList != null && !contactArtifactList.isEmpty()) {
try {
BlackboardArtifact contactArtifact = contactArtifactList.get(0);
BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE);
if (attribute != null) {
contactName = attribute.getValueString();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex);
}
}
return Bundle.AccountInstanceNode_Tooltip_Template(contactName, personaName);
return RelationshipsNodeUtilities.getAccoutToolTipText(getDisplayName(), account);
}
}

View File

@ -1,8 +1,3 @@
# {0} - PersonaAccount count
AccountInstanceNode_Tooltip_suffix=(1 of {0})
# {0} - Contact Name
# {1} - Persona Name
AccountInstanceNode_Tooltip_Template=Contact: {0} - Persona: {1}
AccountNode.accountName=Account
AccountNode.accountType=Type
AccountNode.device=Device

View File

@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.Account;
* PersonaAccounts for a given Account typeSpecificID retrieved on first access
* and evicted from the cache after 5 minutes.
*/
final class CVTPersonaCache {
final public class CVTPersonaCache {
private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName());
private final LoadingCache<Account, List<PersonaAccount>> accountMap;
@ -90,7 +90,7 @@ final class CVTPersonaCache {
*
* @throws ExecutionException
*/
static synchronized List<PersonaAccount> getPersonaAccounts(Account account) throws ExecutionException {
static public synchronized List<PersonaAccount> getPersonaAccounts(Account account) throws ExecutionException {
return getInstance().accountMap.get(account);
}
}

View File

@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* expires after 10 of non-use.
*
*/
final class ContactCache {
final public class ContactCache {
private static final Logger logger = Logger.getLogger(ContactCache.class.getName());
@ -66,7 +66,7 @@ final class ContactCache {
*
* @throws ExecutionException
*/
static synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
static public synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID());
}

View File

@ -0,0 +1,52 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.communications.relationships;
import java.lang.reflect.InvocationTargetException;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
/**
* A subclass of NodeProperty that stores an account object for use with looking
* up personas.
*/
class AccountNodeProperty<T> extends NodeProperty<T> {
private final Account account;
AccountNodeProperty(String name, String displayName, T value, Account account) {
super(name, displayName, "", value);
this.account = account;
}
@Override
public String getShortDescription() {
try {
if (account != null) {
return RelationshipsNodeUtilities.getAccoutToolTipText(getValue().toString(), account);
}
return getValue().toString();
} catch (IllegalAccessException | InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
return "";
}
}

View File

@ -35,6 +35,11 @@ MessageViewer_viewMessage_all=All
MessageViewer_viewMessage_calllogs=Call Logs
MessageViewer_viewMessage_selected=Selected
MessageViewer_viewMessage_unthreaded=Unthreaded
# {0} - PersonaAccount count
RelationshipsNodeUtilities_Tooltip_suffix=(1 of {0})
# {0} - Contact Name
# {1} - Persona Name
RelationshipsNodeUtilities_Tooltip_Template=Contact: {0} - Persona: {1}
# {0} - accountIdentifer
SummaryPersonaPane_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository.
SummaryViewer.countsPanel.border.title=Communications

View File

@ -77,7 +77,16 @@ final class CallLogNode extends BlackboardArtifactNode {
sheetSet.put(createNode(TSK_DATETIME_START, artifact));
sheetSet.put(createNode(TSK_DIRECTION, artifact));
sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", getPhoneNumber(artifact)));
String phoneNumber = getPhoneNumber(artifact);
Account account = null;
try {
account = artifact.getSleuthkitCase().getCommunicationsManager().getAccount(Account.Type.PHONE, phoneNumber);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get instance of communications manager", ex);
}
sheetSet.put(new AccountNodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), phoneNumber, account));
if(duration != -1) {
sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration)));
}

View File

@ -24,6 +24,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.table.TableColumn;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
@ -82,6 +83,8 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs());
// If changing the order of these columns effects the location of the
// phone number column be sure to adjust the renderer code below.
outlineViewPanel.getOutlineView().setPropertyColumns(
TSK_DIRECTION.getLabel(), TSK_DIRECTION.getDisplayName(),
TSK_PHONE_NUMBER.getLabel(), Bundle.CallLogViewer_recipient_label(),
@ -116,6 +119,9 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
updateOutlineViewPanel();
}
});
TableColumn column = outline.getColumnModel().getColumn(2);
column.setCellRenderer(new NodeTableCellRenderer() );
}
@ -223,7 +229,7 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane bottomScrollPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;

View File

@ -24,7 +24,9 @@ import javax.swing.AbstractAction;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -38,9 +40,11 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUB
import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.datamodel.Account;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
@ -113,22 +117,48 @@ class MessageNode extends BlackboardArtifactNode {
String msg_from = getAttributeDisplayString(artifact, TSK_EMAIL_FROM);
String msg_to = getAttributeDisplayString(artifact, TSK_EMAIL_TO);
String date = getAttributeDisplayString(artifact, TSK_DATETIME_SENT);
if (msg_from.isEmpty()) {
msg_from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
}
if (msg_to.isEmpty()) {
msg_to = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
}
if (date.isEmpty()) {
date = getAttributeDisplayString(artifact, TSK_DATETIME);
Account account_from = null;
Account account_to = null;
try {
CommunicationsManager manager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
if (msg_from.isEmpty()) {
msg_from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
if(manager != null && !msg_from.isEmpty()) {
account_from = manager.getAccount(Account.Type.PHONE, msg_from);
}
} else if(manager != null) {
// To email address sometime is in the format <name>: <email>
String toStr = msg_to;
String[] strSplit = msg_to.split(":");
if(strSplit.length > 0) {
toStr = strSplit[strSplit.length-1].trim();
}
account_from = manager.getAccount(Account.Type.EMAIL, toStr);
}
if (msg_to.isEmpty()) {
msg_to = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
if(manager != null && !msg_to.isEmpty()) {
account_to = manager.getAccount(Account.Type.PHONE, msg_to);
}
} else if(manager != null) {
account_to = manager.getAccount(Account.Type.EMAIL, msg_to);
}
if (date.isEmpty()) {
date = getAttributeDisplayString(artifact, TSK_DATETIME);
}
} catch (TskCoreException ex) {
}
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
msg_from)); //NON-NLS
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
msg_to)); //NON-NLS
sheetSet.put(new AccountNodeProperty<>("From", Bundle.MessageNode_Node_Property_From(),
msg_from, account_from)); //NON-NLS
sheetSet.put(new AccountNodeProperty<>("To", Bundle.MessageNode_Node_Property_To(),
msg_to, account_to)); //NON-NLS
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
date)); //NON-NLS

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -25,6 +25,7 @@ import java.beans.PropertyChangeListener;
import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
@ -65,6 +66,8 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
outline = outlineViewPanel.getOutlineView().getOutline();
// When changing this column this, if the from and to columns pos is
// effected make sure to modify the renderer code below.
outlineViewPanel.getOutlineView().setPropertyColumns(
"From", Bundle.MessageViewer_columnHeader_From(),
"To", Bundle.MessageViewer_columnHeader_To(),
@ -98,7 +101,13 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
}
}
});
TableColumn column = outline.getColumnModel().getColumn(1);
column.setCellRenderer(new NodeTableCellRenderer());
column = outline.getColumnModel().getColumn(2);
column.setCellRenderer(new NodeTableCellRenderer());
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
outlineViewPanel.setTableColumnsWidth(5, 10, 10, 15, 50, 10);
@ -164,7 +173,7 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()), true));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.communications.relationships;
import java.awt.Component;
import java.beans.FeatureDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* TableCellRenderer for NodeProperty with custom tooltip data.
*/
final class NodeTableCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(NodeTableCellRenderer.class.getName());
@Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
String descr = "";
Object theRealValue = value;
if (value instanceof NodeProperty) {
descr = ((FeatureDescriptor) value).getShortDescription();
try {
theRealValue = ((Node.Property<?>) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, "Unable to get NodeProperty cell value.");
}
}
super.getTableCellRendererComponent(table, theRealValue, isSelected, hasFocus, row, column);
setToolTipText(descr);
return this;
}
}

View File

@ -19,12 +19,21 @@
package org.sleuthkit.autopsy.communications.relationships;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.communications.CVTPersonaCache;
import org.sleuthkit.autopsy.communications.ContactCache;
import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
@ -33,9 +42,11 @@ import org.sleuthkit.datamodel.TskCoreException;
* A set of reusable utility functions for the Relationships package.
*
*/
final class RelationshipsNodeUtilities {
final public class RelationshipsNodeUtilities {
private static final Logger logger = Logger.getLogger(RelationshipsNodeUtilities.class.getName());
private static final BlackboardAttribute.Type NAME_ATTRIBUTE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID()));
// Here to make codacy happy
private RelationshipsNodeUtilities(){
@ -69,4 +80,52 @@ final class RelationshipsNodeUtilities {
}
}
@NbBundle.Messages({
"# {0} - Contact Name",
"# {1} - Persona Name",
"RelationshipsNodeUtilities_Tooltip_Template=Contact: {0} - Persona: {1}",
"# {0} - PersonaAccount count",
"RelationshipsNodeUtilities_Tooltip_suffix=(1 of {0})"
})
static public String getAccoutToolTipText(String displayName, Account account) {
if(account == null) {
return displayName;
}
List<PersonaAccount> personaList;
List<BlackboardArtifact> contactArtifactList;
try {
personaList = CVTPersonaCache.getPersonaAccounts(account);
contactArtifactList = ContactCache.getContacts(account);
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
return displayName;
}
String personaName;
if (personaList != null && !personaList.isEmpty()) {
personaName = personaList.get(0).getPersona().getName();
if (personaList.size() > 1) {
personaName += Bundle.RelationshipsNodeUtilities_Tooltip_suffix(Integer.toString(personaList.size()));
}
} else {
personaName = "None";
}
String contactName = displayName;
if (contactArtifactList != null && !contactArtifactList.isEmpty()) {
try {
BlackboardArtifact contactArtifact = contactArtifactList.get(0);
BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE);
if (attribute != null) {
contactName = attribute.getValueString();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex);
}
}
return Bundle.RelationshipsNodeUtilities_Tooltip_Template(contactName, personaName);
}
}