mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge remote-tracking branch 'sleuthkit/develop' into 6445_persona_confidence_update
This commit is contained in:
commit
df5891f552
@ -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: \\ : * ? " < > | , ;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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<>();
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -53,3 +53,4 @@ AddAccountDialog.confidenceLbl.text=Confidence:
|
||||
AddAccountDialog.typeLbl.text=Type:
|
||||
AddAccountDialog.identiferLbl.text=Identifier:
|
||||
AddAccountDialog.identifierTextField.text=
|
||||
PersonaManagerTopComponent.deleteBtn.text=Delete Persona
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
96
Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java
Executable file
96
Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java
Executable 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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user