Merge remote-tracking branch 'sleuthkit/develop' into 6445_persona_confidence_update

This commit is contained in:
Ethan Roseman 2020-06-10 13:42:16 -04:00
commit df5891f552
16 changed files with 481 additions and 168 deletions

View File

@ -1,4 +1,4 @@
OptionsCategory_Name_TagNamesOptions=Tags
OptionsCategory_Name_TagNamesOptions=Custom Tags
OptionsCategory_TagNames=TagNames
TagNameDialog.title.text=New Tag
TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols: \\ : * ? " < > | , ;

View File

@ -1,4 +1,4 @@
OptionsCategory_Name_TagNamesOptions=Tags
OptionsCategory_Name_TagNamesOptions=Custom Tags
OptionsCategory_TagNames=TagNames
TagNameDefinition.predefTagNames.bookmark.text=Bookmark
TagNameDefinition.predefTagNames.followUp.text=Follow Up

View File

@ -291,6 +291,14 @@ public class Persona {
public void removeAccount(PersonaAccount account) throws CentralRepoException {
PersonaAccount.removePersonaAccount(account.getId());
}
/**
* Marks this persona as deleted
*/
public void delete() throws CentralRepoException {
String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id;
CentralRepository.getInstance().executeUpdateSQL(deleteSQL);
}
/**
* Callback to process a Persona query from the persona table.
@ -362,7 +370,8 @@ public class Persona {
}
/**
* Gets the rows from the Personas table with matching name.
* Gets the rows from the Personas table with matching name.
* Persona marked as DELETED are not returned.
*
* @param partialName Name substring to match.
* @return Collection of personas matching the given name substring, may be
@ -374,7 +383,8 @@ public class Persona {
public static Collection<Persona> getPersonaByName(String partialName) throws CentralRepoException {
String queryClause = PERSONA_QUERY
+ "WHERE LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
+ "WHERE p.status_id != " + PersonaStatus.DELETED.status_id +
" AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
@ -668,7 +678,8 @@ public class Persona {
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE case_id = " + correlationCase.getID();
+ " WHERE case_id = " + correlationCase.getID()
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
@ -699,7 +710,8 @@ public class Persona {
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE data_source_id = " + dataSource.getID();
+ " WHERE data_source_id = " + dataSource.getID()
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);

View File

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Account;
/**
* This class represents an association between a Persona and an Account.
@ -130,7 +131,13 @@ public class PersonaAccount {
* account.
*/
static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer currentExaminer = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
CentralRepository cr = CentralRepository.getInstance();
if(cr == null) {
throw new CentralRepoException("Failed to add Persona, Central Repository is not enable");
}
CentralRepoExaminer currentExaminer = cr.getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@ -235,38 +242,53 @@ public class PersonaAccount {
* Gets all the Accounts for the specified Persona.
*
* @param personaId Id of persona for which to get the accounts for.
*
* @return Collection of PersonaAccounts, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
* persona_account.
*/
static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.persona_id = " + personaId;
CentralRepository cr = CentralRepository.getInstance();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
if (cr != null) {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.persona_id = " + personaId;
return queryCallback.getPersonaAccountsList();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
}
/**
* Gets all the Persona for the specified Account.
*
* @param accountId Id of account for which to get the Personas for.
*
* @return Collection of PersonaAccounts. may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
* persona_account.
*/
public static Collection<PersonaAccount> getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.account_id = " + accountId;
+ " WHERE persona_accounts.account_id = " + accountId
+ "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
CentralRepository cr = CentralRepository.getInstance();
return queryCallback.getPersonaAccountsList();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
}
/**
@ -274,20 +296,51 @@ public class PersonaAccount {
* account identifier substring.
*
* @param accountIdentifierSubstring Account identifier substring to search
* for.
* for.
*
* @return Collection of PersonaAccounts. may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
* persona_account.
*/
public static Collection<PersonaAccount> getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')";
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"
+ "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
}
/**
* Gets all the Persona associated with the given account.
*
* @param account Account to search for.
*
* @return Collection of PersonaAccounts, maybe empty if none were found or
* CR is not enabled.
*
* @throws CentralRepoException
*/
public static Collection<PersonaAccount> getPersonaAccountsForAccount(Account account) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + account.getTypeSpecificID() + "%') AND type_name = '" + account.getAccountType().getTypeName() + "' ";
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
}
/**
@ -299,8 +352,14 @@ public class PersonaAccount {
* account.
*/
static void removePersonaAccount(long id) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
if(cr == null) {
throw new CentralRepoException("Failed to remove persona account, Central Repo is not enabled");
}
String deleteClause = " DELETE FROM persona_accounts WHERE id = " + id;
CentralRepository.getInstance().executeDeleteSQL(deleteClause);
cr.executeDeleteSQL(deleteClause);
}
/**
@ -343,17 +402,23 @@ public class PersonaAccount {
* accounts.
*/
static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException {
String queryClause = "SELECT account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
+ " account_types.type_name as type_name "
+ " FROM persona_accounts "
+ " JOIN accounts as accounts on persona_accounts.account_id = accounts.id "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " WHERE persona_accounts.persona_id = " + personaId;
CentralRepository cr = CentralRepository.getInstance();
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
if (cr != null) {
String queryClause = "SELECT account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
+ " account_types.type_name as type_name "
+ " FROM persona_accounts "
+ " JOIN accounts as accounts on persona_accounts.account_id = accounts.id "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " WHERE persona_accounts.persona_id = " + personaId;
return queryCallback.getAccountsList();
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getAccountsList();
}
return new ArrayList<>();
}
}

View File

@ -343,93 +343,6 @@ public class IngestEventsListener {
event = evt;
}
/**
* Automatically creates personas from all the TSK_CONTACT artifacts
* found in a data source.
*
* @param dataSource Data source that was just analyzed.
* @throws TskCoreException If there is any error getting contact
* artifacts from case database.
* @throws CentralRepoException If there is an error in creating
* personas in the Central Repo.
*/
private void autoGenerateContactPersonas(Content dataSource) throws TskCoreException, CentralRepoException {
Blackboard blackboard;
try {
blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
return;
}
// get all TSK_CONTACT artifacts in this data source.
List<BlackboardArtifact> contactArtifacts = blackboard.getArtifacts(TSK_CONTACT.getTypeID(), dataSource.getId());
for (BlackboardArtifact artifact : contactArtifacts) {
BlackboardAttribute nameAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
String personaName = (nameAttr != null) ? nameAttr.getValueString() : null;
// Get phone number and email attributes.
BlackboardAttribute phoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER));
BlackboardAttribute homePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME));
BlackboardAttribute mobilePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE));
BlackboardAttribute emailAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL));
Persona persona = personaFromContactAttribute(null, Account.Type.PHONE, phoneAttr, personaName);
persona = personaFromContactAttribute(persona, Account.Type.PHONE, homePhoneAttr, personaName);
persona = personaFromContactAttribute(persona, Account.Type.PHONE, mobilePhoneAttr, personaName);
personaFromContactAttribute(persona, Account.Type.EMAIL, emailAttr, personaName);
}
}
/**
* Gets central repo account for the given attribute for a TSK_CONTACT
* artifact. Associates the given persona with that account. Creates a
* Persona, if one isn't provided.
*
* @param persona Persona to associate with the account. May be null, in
* which case a persona is created first.
* @param accountType Account type of account to be associated.
* @param attribute Attribute form which get the account id.
* @param personaName Persona name, if a persona needs to be created.
* @return Persona created or associated with the account.
*
* @throws TskCoreException If there is an error in normalizing the
* account id.
* @throws CentralRepoException If there is an erorr is getting the
* account or associating the persona with it.
*/
private Persona personaFromContactAttribute(Persona persona, Account.Type accountType, BlackboardAttribute attribute, String personaName) throws CentralRepoException, TskCoreException {
Persona personaToReturn = persona;
if (attribute != null) {
String accountId = attribute.getValueString();
if (CommunicationsUtils.isValidAccountId(accountType, accountId)) {
if (accountType == Account.Type.PHONE) {
accountId = CommunicationsUtils.normalizePhoneNum(accountId);
} else if (accountType == Account.Type.EMAIL) {
accountId = CommunicationsUtils.normalizeEmailAddress(accountId);
}
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountType.getTypeName());
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountId);
PersonaAccount personaAccount;
// If persona doesnt exist, create one
if (persona == null) {
personaToReturn = Persona.createPersonaForAccount(personaName, "Auto generated contact persona", Persona.PersonaStatus.UNKNOWN, crAccount, "Found in contact book entry", Persona.Confidence.HIGH);
} else {
persona.addAccount(crAccount, "Found in contact book entry", Persona.Confidence.HIGH);
}
}
}
return personaToReturn;
}
@Override
public void run() {
// clear the tracker to reduce memory usage
@ -504,8 +417,6 @@ public class IngestEventsListener {
correlationDataSource.setSha256(imageSha256Hash);
}
}
// automatically generate persona from contact artifacts.
autoGenerateContactPersonas(dataSource);
}
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, String.format(

View File

@ -53,3 +53,4 @@ AddAccountDialog.confidenceLbl.text=Confidence:
AddAccountDialog.typeLbl.text=Type:
AddAccountDialog.identiferLbl.text=Identifier:
AddAccountDialog.identifierTextField.text=
PersonaManagerTopComponent.deleteBtn.text=Delete Persona

View File

@ -1,18 +1,38 @@
AddAccountDialog.title.text=Add Account
AddAccountDialog_dup_msg=This account is already added to the persona
AddAccountDialog_dup_Title=Account add failure
AddAccountDialog_empty_msg=The identifier field cannot be empty
AddAccountDialog_empty_Title=Empty identifier
AddAccountDialog_get_types_exception_msg=Failed to access central repository
AddAccountDialog_get_types_exception_Title=Central Repository failure
AddAccountDialog_validate_id_failure=Account ID must not be empty
AddAccountDialog_validate_id_failure_title=Account ID issue
AddAccountDialog_search_empty_msg=Account not found for given identifier and type
AddAccountDialog_search_empty_Title=Account not found
AddAccountDialog_search_failure_msg=Central Repository account search failed
AddAccountDialog_search_failure_Title=Account add failure
AddAliasDialog.title.text=Add Alias
AddAliasDialog_dup_msg=This alias has already been added to this persona
AddAliasDialog_dup_Title=Alias add failure
AddMetadataDialog.title.text=Add Metadata
AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona
AddMetadataDialog_dup_Title=Metadata add failure
CTL_OpenPersonaManager=Persona Manager
CTL_PersonaManagerTopComponentAction=Persona Manager
CTL_PersonaDetailsTopComponent=Persona Details
OpenPersonasAction.displayName=Persona Manager
PersonaDetailsDialogCreateTitle=Create Persona
PersonaDetailsDialogEditTitle=Edit Persona
PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository
PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure
PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty
PersonaDetailsPanel_EmptyName_Title=Empty persona name
PersonaDetailsPanel_load_exception_msg=Failed to load persona
PersonaDetailsPanel_load_exception_Title=Initialization failure
PersonaDetailsPanel_NameCreate=Create Persona
PersonaDetailsPanel_NameEdit=Edit Persona
PersonaDetailsPanel_NameView=View Persona
PersonaManagerTopComponent.createBtn.text=Create New
PersonaDetailsPanel_NotEnoughAccounts_msg=Two or more accounts are necessary to create a persona
PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts
PersonaManagerTopComponent.createBtn.text=New Persona
PersonaManagerTopComponent.searchBtn.text=Search
PersonaManagerTopComponent.resultsTable.columnModel.title1=Name
PersonaManagerTopComponent.resultsTable.columnModel.title0=ID
@ -20,6 +40,17 @@ PersonaManagerTopComponent.resultsTable.toolTipText=
PersonaManagerTopComponent.searchAccountRadio.text=Account
PersonaManagerTopComponent.searchNameRadio.text=Name
PersonaManagerTopComponent.searchField.text=
AddAccountDialog.cancelBtn.text=Cancel
AddAccountDialog.okBtn.text=OK
PersonaManagerTopComponent.editBtn.text=Edit Persona
PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsPanel.deleteCaseBtn.text=Delete
PersonaDetailsPanel.addCaseBtn.text=Add
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
PersonaDetailsPanel.aliasesLabel.text=Aliases:
PersonaDetailsPanel.deleteMetadataBtn.text=Delete
PersonaDetailsPanel.addMetadataBtn.text=Add
PersonaDetailsPanel.metadataLabel.text=Metadata:
@ -27,18 +58,32 @@ PersonaDetailsPanel.deleteAccountBtn.text=Delete
PersonaDetailsPanel.addAccountBtn.text=Add
PersonaDetailsPanel.accountsLbl.text=Accounts:
PersonaDetailsPanel.nameField.text=
PersonaDetailsPanel.saveBtn.toolTipText=
PersonaDetailsPanel.saveBtn.text=Save Changes
PersonaDetailsPanel.nameLbl.text=Name:
PersonaDetailsPanel.deleteCaseBtn.text=Delete
PersonaDetailsPanel.addCaseBtn.text=Add
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
PersonaDetailsPanel.aliasesLabel.text=Aliases:
AddAccountDialog.cancelBtn.text=Cancel
AddAccountDialog.okBtn.text=OK
PersonaManagerTopComponent.editBtn.text=Edit
AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel
AddMetadataDialog.cancelBtn.text=Cancel
AddMetadataDialog.okBtn.text=OK
AddMetadataDialog.nameLbl.text=Name:
AddMetadataDialog.nameTextField.text=
AddMetadataDialog.valueLbl.text=Value:
AddMetadataDialog.valueTextField.text=
AddMetadataDialog.justificationLbl.text=Justification:
AddMetadataDialog.justificationTextField.text=
AddMetadataDialog.confidenceLbl.text=Confidence:
AddAliasDialog.justificationLbl.text=Justification:
AddAliasDialog.okBtn.text_1=OK
AddAliasDialog.cancelBtn.text_1=Cancel
AddAliasDialog.confidenceLbl.text=Confidence:
AddAliasDialog.justificationTextField.text=
AddAliasDialog.aliasLbl.text=Alias:
AddAliasDialog.aliasTextField.text=
AddAccountDialog.justificationTextField.text=
AddAccountDialog.justificationLbl.text=Justification:
AddAccountDialog.confidenceLbl.text=Confidence:
AddAccountDialog.typeLbl.text=Type:
AddAccountDialog.identiferLbl.text=Identifier:
AddAccountDialog.identifierTextField.text=
PMTopComponent_Name=Persona Manager
PMTopComponent_search_exception_msg=Failed to search personas
PMTopComponent_search_exception_Title=Search failure

View File

@ -182,7 +182,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
}
}
return false;
}
}
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
if (!accountExists(account)) {
@ -560,7 +560,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
void clear() {
currentPersona = null;
nameField.setText(Persona.getDefaultName());
nameField.setText(mode == PersonaDetailsMode.CREATE ? Persona.getDefaultName() : "");
currentAccounts = new ArrayList<>();
currentMetadata = new ArrayList<>();
currentAliases = new ArrayList<>();
@ -714,8 +714,8 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
}
@Messages({
"PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs two or more accounts",
"PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts",
"PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account",
"PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account",
"PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository",
"PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure",
"PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty",

View File

@ -77,7 +77,8 @@
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="resultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/>
@ -110,6 +111,7 @@
<Group type="103" groupAlignment="3" attributes="0">
<Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="createBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -208,6 +210,14 @@
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="deleteBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.deleteBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">

View File

@ -54,7 +54,9 @@ public final class PersonaManagerTopComponent extends TopComponent {
private Persona selectedPersona = null;
@Messages({
"PMTopComponent_Name=Persona Manager"
"PMTopComponent_Name=Persona Manager",
"PMTopComponent_delete_exception_Title=Delete failure",
"PMTopComponent_delete_exception_msg=Failed to delete persona",
})
public PersonaManagerTopComponent() {
initComponents();
@ -83,6 +85,25 @@ public final class PersonaManagerTopComponent extends TopComponent {
PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl());
}
});
deleteBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
if (selectedPersona != null) {
selectedPersona.delete();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex);
JOptionPane.showMessageDialog(PersonaManagerTopComponent.this,
Bundle.PMTopComponent_delete_exception_msg(),
Bundle.PMTopComponent_delete_exception_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
executeSearch();
}
});
// Results table
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@ -116,6 +137,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
Persona persona = currentResults.get(index);
selectedPersona = persona;
editBtn.setEnabled(true);
deleteBtn.setEnabled(true);
}
/**
@ -147,6 +169,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
if (selectedRow != -1) {
setPersona(resultsTable.getSelectedRow());
detailsPanel.setMode(this, PersonaDetailsMode.VIEW, selectedPersona);
} else {
detailsPanel.clear();
}
}
@ -189,6 +213,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
resultsTable.clearSelection();
updateResultsTable(results);
editBtn.setEnabled(false);
deleteBtn.setEnabled(false);
}
@Override
@ -216,6 +241,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
searchBtn = new javax.swing.JButton();
editBtn = new javax.swing.JButton();
createBtn = new javax.swing.JButton();
deleteBtn = new javax.swing.JButton();
detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
setMinimumSize(new java.awt.Dimension(400, 400));
@ -246,6 +272,9 @@ public final class PersonaManagerTopComponent extends TopComponent {
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.createBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.deleteBtn.text")); // NOI18N
deleteBtn.setEnabled(false);
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
@ -257,7 +286,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addGap(0, 0, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(deleteBtn))
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
@ -283,7 +313,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn)
.addComponent(createBtn))
.addComponent(createBtn)
.addComponent(deleteBtn))
.addContainerGap())
);
@ -304,6 +335,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton createBtn;
private javax.swing.JButton deleteBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JButton editBtn;
private javax.swing.JSplitPane jSplitPane1;

View File

@ -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<PersonaAccount> personaList;
try {
personaList = CVTPersonaCache.getPersonaAccounts(account);
} 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);
}
}

View File

@ -0,0 +1,96 @@
/*
* 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;
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;
import org.sleuthkit.datamodel.Account;
/**
* 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<Account, List<PersonaAccount>> accountMap;
private static CVTPersonaCache instance;
/**
* Cache constructor.
*/
private CVTPersonaCache() {
accountMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
new CacheLoader<Account, List<PersonaAccount>>() {
@Override
public List<PersonaAccount> load(Account key) {
List<PersonaAccount> accountList = new ArrayList<>();
try {
if (CentralRepository.isEnabled()) {
Collection<PersonaAccount> accounts = PersonaAccount.getPersonaAccountsForAccount(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<PersonaAccount> getPersonaAccounts(Account account) throws ExecutionException {
return getInstance().accountMap.get(account);
}
}

View File

@ -1,7 +1,7 @@
AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u6642\u523b
AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9
AbstractAbstractFileNode.changeTimeColLbl=\u6642\u523b\u5909\u66f4
AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u3057\u305f\u6642\u523b
AbstractAbstractFileNode.changeTimeColLbl=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u65e5\u6642
AbstractAbstractFileNode.createSheet.comment.displayName=C
AbstractAbstractFileNode.createSheet.comment.name=C
# {0} - occurrenceCount
@ -26,7 +26,7 @@ AbstractAbstractFileNode.md5HashColLbl=MD5\u30cf\u30c3\u30b7\u30e5
AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30a2\u30c9\u30ec\u30b9
AbstractAbstractFileNode.mimeType=MIME\u30bf\u30a4\u30d7
AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9
AbstractAbstractFileNode.modifiedTimeColLbl=MFT\u5909\u66f4\u6642\u523b
AbstractAbstractFileNode.modifiedTimeColLbl=\u66f4\u65b0\u65e5\u6642
AbstractAbstractFileNode.nameColLbl=\u540d\u524d
AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID
AbstractAbstractFileNode.originalName=\u30aa\u30ea\u30b8\u30ca\u30eb\u540d
@ -136,14 +136,14 @@ ImageNode.createSheet.type.name=\u30bf\u30a4\u30d7
ImageNode.createSheet.type.text=\u30a4\u30e1\u30fc\u30b8
ImageNode.getActions.openFileSearchByAttr.text=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f
KeyValueNode.menuItemText.viewFileInDir=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a
KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u6642\u523b
KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u6642\u523b
KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
KeywordHits.createNodeForKey.accessTime.name=AccessTime
KeywordHits.createNodeForKey.chgTime.desc=\u6642\u523b\u5909\u66f4
KeywordHits.createNodeForKey.chgTime.displayName=\u5909\u66f4\u6642\u523b
KeywordHits.createNodeForKey.chgTime.desc=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
KeywordHits.createNodeForKey.chgTime.displayName=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
KeywordHits.createNodeForKey.chgTime.name=ChangeTime
KeywordHits.createNodeForKey.modTime.desc=MFT\u5909\u66f4\u6642\u523b
KeywordHits.createNodeForKey.modTime.displayName=MFT\u5909\u66f4\u6642\u523b
KeywordHits.createNodeForKey.modTime.desc=\u66f4\u65b0\u65e5\u6642
KeywordHits.createNodeForKey.modTime.displayName=\u66f4\u65b0\u65e5\u6642
KeywordHits.createNodeForKey.modTime.name=ModifiedTime
KeywordHits.createSheet.filesWithHits.desc=\u8aac\u660e\u306a\u3057
KeywordHits.createSheet.filesWithHits.displayName=\u30d2\u30c3\u30c8\u306e\u3042\u308b\u30d5\u30a1\u30a4\u30eb
@ -199,14 +199,14 @@ ContentTagNode.createSheet.filePath.name=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
ContentTagNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
ContentTagNode.createSheet.comment.name=\u30b3\u30e1\u30f3\u30c8
ContentTagNode.createSheet.comment.displayName=\u30b3\u30e1\u30f3\u30c8
ContentTagNode.createSheet.fileModifiedTime.nam=MFT\u5909\u66f4\u6642\u523b
ContentTagNode.createSheet.fileModifiedTime.displayName=MFT\u5909\u66f4\u6642\u523b
ContentTagNode.createSheet.fileChangedTime.name=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b
ContentTagNode.createSheet.fileChangedTime.displayName=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b
ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b
ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b
ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u3057\u305f\u6642\u523b
ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u3057\u305f\u6642\u523b
ContentTagNode.createSheet.fileModifiedTime.nam=\u66f4\u65b0\u65e5\u6642
ContentTagNode.createSheet.fileModifiedTime.displayName=\u66f4\u65b0\u65e5\u6642
ContentTagNode.createSheet.fileChangedTime.name=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
ContentTagNode.createSheet.fileChangedTime.displayName=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u65e5\u6642
ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u65e5\u6642
ContentTagNode.createSheet.fileSize.name=\u30b5\u30a4\u30ba
ContentTagNode.createSheet.fileSize.displayName=\u30b5\u30a4\u30ba
ContentTagTypeNode.displayName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0

View File

@ -141,10 +141,10 @@ ReportGenerator.progress.createdThumb.text=\u30b5\u30e0\u30cd\u30a4\u30eb\u306e\
ReportGenerator.htmlOutput.header.file=\u30d5\u30a1\u30a4\u30eb
ReportGenerator.htmlOutput.header.tag=\u30bf\u30b0
ReportGenerator.htmlOutput.header.comment=\u30b3\u30e1\u30f3\u30c8
ReportGenerator.htmlOutput.header.timeModified=MFT\u5909\u66f4\u6642\u523b
ReportGenerator.htmlOutput.header.timeChanged=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b
ReportGenerator.htmlOutput.header.timeAccessed=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b
ReportGenerator.htmlOutput.header.timeCreated=\u4f5c\u6210\u3055\u308c\u305f\u6642\u523b
ReportGenerator.htmlOutput.header.timeModified=\u66f4\u65b0\u65e5\u6642
ReportGenerator.htmlOutput.header.timeChanged=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642
ReportGenerator.htmlOutput.header.timeAccessed=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
ReportGenerator.htmlOutput.header.timeCreated=\u4f5c\u6210\u65e5\u6642
ReportGenerator.htmlOutput.header.size=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8)
ReportGenerator.htmlOutput.header.hash=\u30cf\u30c3\u30b7\u30e5
ReportGenerator.thumbnailTable.name=\u30bf\u30b0\u4ed8\u304d\u30a4\u30e1\u30fc\u30b8

View File

@ -95,6 +95,12 @@ final public class EventTypeUtils {
imageFileName = "artifact-icon.png";
} else if (typeID == TimelineEventType.WEB_FORM_ADDRESSES.getTypeID()) {
imageFileName = "artifact-icon.png";
} else if (typeID == TimelineEventType.METADATA_CREATED.getTypeID()) {
imageFileName = "blue-document-attribute-b.png";
} else if (typeID == TimelineEventType.METADATA_LAST_SAVED.getTypeID()) {
imageFileName = "blue-document-attribute-m.png";
} else if (typeID == TimelineEventType.METADATA_LAST_PRINTED.getTypeID()) {
imageFileName = "blue-document.png";
}else {
imageFileName = "timeline_marker.png";
}

View File

@ -19,11 +19,18 @@
package org.sleuthkit.autopsy.keywordsearch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharSource;
import java.io.IOException;
import java.io.Reader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static java.util.Locale.US;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
@ -54,6 +61,10 @@ import org.sleuthkit.autopsy.textextractors.TextFileExtractor;
import org.sleuthkit.autopsy.textextractors.configs.ImageConfig;
import org.sleuthkit.autopsy.textextractors.configs.StringsConfig;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskData.FileKnown;
@ -114,6 +125,26 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
"application/x-z", //NON-NLS
"application/x-compress"); //NON-NLS
private static final List<String> METADATA_DATE_TYPES
= ImmutableList.of(
"Last-Save-Date", //NON-NLS
"Last-Printed", //NON-NLS
"Creation-Date"); //NON-NLS
private static final Map<String, BlackboardAttribute.ATTRIBUTE_TYPE> METADATA_TYPES_MAP = ImmutableMap.<String, BlackboardAttribute.ATTRIBUTE_TYPE>builder()
.put("Last-Save-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED)
.put("Last-Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID)
.put("Creation-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED)
.put("Company", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ORGANIZATION)
.put("Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OWNER)
.put("Application-Name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME)
.put("Last-Printed", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LAST_PRINTED_DATETIME)
.put("Producer", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME)
.put("Title", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION)
.put("pdf:PDFVersion", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VERSION)
.build();
/**
* Options for this extractor
*/
@ -493,6 +524,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
Reader finalReader;
try {
Map<String, String> metadata = extractor.getMetadata();
if (!metadata.isEmpty()) {
createMetadataArtifact(aFile, metadata);
}
CharSource formattedMetadata = getMetaDataCharSource(metadata);
//Append the metadata to end of the file text
finalReader = CharSource.concat(new CharSource() {
@ -515,7 +549,70 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
return false;
}
}
private void createMetadataArtifact(AbstractFile aFile, Map<String, String> metadata) {
String moduleName = KeywordSearchIngestModule.class.getName();
Collection<BlackboardAttribute> attributes = new ArrayList<>();
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
for (Map.Entry<String, String> entry : metadata.entrySet()) {
if (METADATA_TYPES_MAP.containsKey(entry.getKey())) {
BlackboardAttribute bba = checkAttribute(entry.getKey(), entry.getValue());
if (bba != null) {
attributes.add(bba);
}
}
}
if (!attributes.isEmpty()) {
try {
BlackboardArtifact bbart = aFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA);
bbart.addAttributes(attributes);
bbartifacts.add(bbart);
} catch (TskCoreException ex) {
// Log error and return to continue processing
logger.log(Level.WARNING, String.format("Error creating or adding metadata artifact for file %s.", aFile.getParentPath() + aFile.getName()), ex); //NON-NLS
return;
}
if (!bbartifacts.isEmpty()) {
try{
Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().postArtifacts(bbartifacts, moduleName);
} catch (NoCurrentCaseException | Blackboard.BlackboardException ex) {
// Log error and return to continue processing
logger.log(Level.WARNING, String.format("Unable to post blackboard artifacts for file $s.", aFile.getParentPath() + aFile.getName()) , ex); //NON-NLS
return;
}
}
}
}
private BlackboardAttribute checkAttribute(String key, String value) {
String moduleName = KeywordSearchIngestModule.class.getName();
if (!value.isEmpty() && value.charAt(0) != ' ') {
if (METADATA_DATE_TYPES.contains(key)) {
SimpleDateFormat metadataDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", US);
Long metadataDateTime = Long.valueOf(0);
try {
String metadataDate = value.replaceAll("T"," ").replaceAll("Z", "");
Date usedDate = metadataDateFormat.parse(metadataDate);
metadataDateTime = usedDate.getTime()/1000;
return new BlackboardAttribute(METADATA_TYPES_MAP.get(key), moduleName, metadataDateTime);
} catch (ParseException ex) {
// catching error and displaying date that could not be parsed then will continue on.
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for metadata attribute %s.", value, key), ex); //NON-NLS
return null;
}
} else {
return new BlackboardAttribute(METADATA_TYPES_MAP.get(key), moduleName, value);
}
}
return null;
}
/**
* Pretty print the text extractor metadata.
*