feat(persons): identify impersonated identities
This commit is contained in:
parent
18e59cc5e6
commit
a6ec67f38d
20 changed files with 321 additions and 11 deletions
|
|
@ -1,7 +1,7 @@
|
|||
/******************************************************************************
|
||||
* Labfy Investigation
|
||||
*
|
||||
* Extensions idempotentes du schéma SQLite courant V5
|
||||
* Extensions idempotentes du schéma SQLite courant V6
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
|
|
|
|||
38
database/schema_v6.sql
Normal file
38
database/schema_v6.sql
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
* Labfy Investigation
|
||||
*
|
||||
* Migration du schéma SQLite V5 vers V6 : catégorie identité usurpée
|
||||
******************************************************************************/
|
||||
|
||||
ALTER TABLE person_roles RENAME TO person_roles_v5;
|
||||
|
||||
CREATE TABLE person_roles
|
||||
(
|
||||
entity_id TEXT PRIMARY KEY,
|
||||
role TEXT NOT NULL DEFAULT 'uncategorized',
|
||||
updated_at TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (entity_id)
|
||||
REFERENCES entites(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CHECK (role IN
|
||||
(
|
||||
'uncategorized',
|
||||
'alleged_scammer',
|
||||
'victim',
|
||||
'witness',
|
||||
'suspect',
|
||||
'related_person',
|
||||
'impersonated_identity'
|
||||
)),
|
||||
CHECK (length(updated_at) = 20)
|
||||
);
|
||||
|
||||
INSERT INTO person_roles(entity_id, role, updated_at)
|
||||
SELECT entity_id, role, updated_at FROM person_roles_v5;
|
||||
|
||||
DROP TABLE person_roles_v5;
|
||||
|
||||
CREATE INDEX idx_person_roles_role ON person_roles(role);
|
||||
|
|
@ -1161,11 +1161,11 @@ compte via `preuve_entites`. La création des deux lignes et de cette liaison
|
|||
est transactionnelle : aucun nœud incomplet n'est conservé si une étape
|
||||
échoue.
|
||||
|
||||
### Rôles d'enquête des personnes — schéma V5
|
||||
### Rôles d'enquête des personnes — schémas V5 et V6
|
||||
|
||||
La table `person_roles` associe une entité de type personne à une catégorie
|
||||
d'enquête contrôlée : scammer présumé, victime, témoin, suspect, personne liée
|
||||
ou non catégorisée. La clé étrangère avec suppression en cascade évite les
|
||||
d'enquête contrôlée : scammer présumé, victime, témoin, suspect, personne liée,
|
||||
identité usurpée ou non catégorisée. La clé étrangère avec suppression en cascade évite les
|
||||
rôles orphelins. Le code stable est persisté ; le libellé et la couleur restent
|
||||
des choix de présentation.
|
||||
|
||||
|
|
|
|||
|
|
@ -42,5 +42,15 @@ gboolean person_entity_service_update_role(Database *database,
|
|||
*/
|
||||
gboolean person_entity_service_update_confidence(Database *database,
|
||||
const char *entity_identifier, gint confidence, GError **error);
|
||||
/**
|
||||
* @brief Enregistre le nom affiché d'une personne existante.
|
||||
* @param database Connexion ouverte empruntée.
|
||||
* @param entity_identifier UUID de la personne.
|
||||
* @param display_name Nouveau nom non vide.
|
||||
* @param error Destination facultative d'une erreur.
|
||||
* @return TRUE lorsque la modification est validée.
|
||||
*/
|
||||
gboolean person_entity_service_update_display_name(Database *database,
|
||||
const char *entity_identifier, const char *display_name, GError **error);
|
||||
G_END_DECLS
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ bool schema_install_v4(
|
|||
/** @brief Installe la migration des rôles de personnes du schéma V5. */
|
||||
bool schema_install_v5(Database *database);
|
||||
|
||||
/** @brief Installe la catégorie d'identité usurpée du schéma V6. */
|
||||
bool schema_install_v6(Database *database);
|
||||
|
||||
/**
|
||||
* @brief Garantit la présence des extensions du schéma courant V2.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ typedef enum
|
|||
PERSON_ROLE_VICTIM,
|
||||
PERSON_ROLE_WITNESS,
|
||||
PERSON_ROLE_SUSPECT,
|
||||
PERSON_ROLE_RELATED_PERSON
|
||||
PERSON_ROLE_RELATED_PERSON,
|
||||
PERSON_ROLE_IMPERSONATED_IDENTITY
|
||||
} PersonRole;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -170,6 +170,9 @@ typedef void (*MainWindowOsintActionCallback)(
|
|||
const char *target_value,
|
||||
gpointer user_data
|
||||
);
|
||||
/** @brief Callback appelé pour modifier le nom affiché d'une personne. */
|
||||
typedef void (*MainWindowPersonNameCallback)(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de vérification d'une preuve.
|
||||
|
|
@ -287,6 +290,9 @@ void main_window_set_osint_action_callback(
|
|||
MainWindowOsintActionCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
/** @brief Définit le callback de modification du nom affiché. */
|
||||
void main_window_set_person_name_callback(MainWindow *main_window,
|
||||
MainWindowPersonNameCallback callback, gpointer user_data);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback du bouton « Nouvelle enquête ».
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ typedef void (*EntityDetailsPanelPersonConfidenceCallback)(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/** @brief Callback appelé pour enregistrer le nom affiché d'une personne. */
|
||||
typedef void (*EntityDetailsPanelPersonNameCallback)(
|
||||
const char *entity_identifier,
|
||||
const char *display_name,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Crée un volet de détails fermé.
|
||||
*
|
||||
|
|
@ -155,6 +162,13 @@ void entity_details_panel_set_person_confidence_callback(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/** @brief Définit le callback de modification du nom affiché. */
|
||||
void entity_details_panel_set_person_name_callback(
|
||||
EntityDetailsPanel *details_panel,
|
||||
EntityDetailsPanelPersonNameCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Indique si le volet est actuellement ouvert.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ typedef void (*WorkspaceOsintActionCallback)(
|
|||
const char *target_value,
|
||||
gpointer user_data
|
||||
);
|
||||
/** @brief Callback appelé pour modifier le nom affiché d'une personne. */
|
||||
typedef void (*WorkspacePersonNameCallback)(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data);
|
||||
|
||||
/** @brief Callback appelé pour modifier les métadonnées d'une preuve. */
|
||||
typedef void (*WorkspaceEditEvidenceCallback)(
|
||||
|
|
@ -365,6 +368,9 @@ void workspace_set_person_confidence_callback(Workspace *workspace,
|
|||
void workspace_set_graph_loading(
|
||||
Workspace *workspace
|
||||
);
|
||||
/** @brief Définit le callback de modification du nom affiché. */
|
||||
void workspace_set_person_name_callback(Workspace *workspace,
|
||||
WorkspacePersonNameCallback callback, gpointer user_data);
|
||||
|
||||
/**
|
||||
* @brief Affiche le graphe chargé avec sa disposition persistée.
|
||||
|
|
|
|||
|
|
@ -5033,6 +5033,35 @@ static void application_on_person_confidence_changed(
|
|||
investigation_project_get_database_path(project));
|
||||
}
|
||||
|
||||
/** @brief Enregistre le nom affiché choisi dans la fiche personne. */
|
||||
static void application_on_person_name_changed(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data)
|
||||
{
|
||||
Application *application = user_data;
|
||||
Database *database = NULL;
|
||||
const InvestigationProject *project = NULL;
|
||||
GError *error = NULL;
|
||||
if (application == NULL || application->session == NULL) return;
|
||||
database = investigation_session_get_database(application->session);
|
||||
project = investigation_session_get_project(application->session);
|
||||
if (!person_entity_service_update_display_name(database, entity_identifier,
|
||||
display_name, &error))
|
||||
{
|
||||
application_present_error(application, "Nom non enregistré",
|
||||
error != NULL ? error->message :
|
||||
"Impossible de modifier le nom affiché de cette personne.");
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
g_free(application->pending_entity_selection_identifier);
|
||||
application->pending_entity_selection_identifier =
|
||||
g_strdup(entity_identifier);
|
||||
main_window_set_status(application->main_window,
|
||||
"Nom enregistré. Actualisation du graphe…");
|
||||
application_start_graph_loading(application,
|
||||
investigation_project_get_database_path(project));
|
||||
}
|
||||
|
||||
/** @brief Libère le contexte d'édition d'une relation. */
|
||||
static void application_edit_relation_context_free(
|
||||
ApplicationEditRelationContext *context)
|
||||
|
|
@ -6705,6 +6734,8 @@ static void application_on_activate(
|
|||
application_on_person_role_changed, application);
|
||||
main_window_set_person_confidence_callback(application->main_window,
|
||||
application_on_person_confidence_changed, application);
|
||||
main_window_set_person_name_callback(application->main_window,
|
||||
application_on_person_name_changed, application);
|
||||
|
||||
main_window_set_osint_action_callback(
|
||||
application->main_window,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,58 @@ static gboolean person_entity_service_status_valid(const char *status)
|
|||
g_strcmp0(status, "confirmed") == 0;
|
||||
}
|
||||
|
||||
gboolean person_entity_service_update_display_name(Database *database,
|
||||
const char *entity_identifier, const char *display_name, GError **error)
|
||||
{
|
||||
static const char sql[] =
|
||||
"UPDATE entites SET label=trim(?), updated_at=? WHERE id=? AND "
|
||||
"type_id=(SELECT id FROM types_entite WHERE code='person');";
|
||||
DatabaseStatement *statement = NULL;
|
||||
GDateTime *now = NULL;
|
||||
char *timestamp = NULL;
|
||||
char *normalized = NULL;
|
||||
gboolean active = FALSE, success = FALSE;
|
||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
||||
normalized = display_name != NULL ? g_strdup(display_name) : NULL;
|
||||
if (normalized != NULL) g_strstrip(normalized);
|
||||
if (database == NULL || entity_identifier == NULL ||
|
||||
!g_uuid_string_is_valid(entity_identifier) || normalized == NULL ||
|
||||
normalized[0] == '\0')
|
||||
{
|
||||
g_set_error_literal(error, g_quark_from_static_string(
|
||||
"person-entity-service-error"), 6,
|
||||
"Le nom affiché de la personne est invalide.");
|
||||
g_free(normalized);
|
||||
return FALSE;
|
||||
}
|
||||
now = g_date_time_new_now_utc();
|
||||
timestamp = now != NULL ? g_date_time_format(now,
|
||||
"%Y-%m-%dT%H:%M:%SZ") : NULL;
|
||||
if (timestamp == NULL || !database_transaction_begin(database))
|
||||
goto cleanup;
|
||||
active = TRUE;
|
||||
statement = database_statement_prepare(database, sql);
|
||||
if (statement == NULL || !database_statement_bind_text(statement, 1,
|
||||
normalized) || !database_statement_bind_text(statement, 2,
|
||||
timestamp) || !database_statement_bind_text(statement, 3,
|
||||
entity_identifier) || database_statement_step(statement) !=
|
||||
DATABASE_STATEMENT_STEP_DONE)
|
||||
goto cleanup;
|
||||
database_statement_finalize(statement); statement = NULL;
|
||||
if (!database_transaction_commit(database)) goto cleanup;
|
||||
active = FALSE; success = TRUE;
|
||||
cleanup:
|
||||
database_statement_finalize(statement);
|
||||
if (!success && active) database_transaction_rollback(database);
|
||||
if (!success && error != NULL && *error == NULL)
|
||||
g_set_error_literal(error, g_quark_from_static_string(
|
||||
"person-entity-service-error"), 7,
|
||||
"Le nom affiché n'a pas pu être enregistré.");
|
||||
g_free(normalized); g_free(timestamp);
|
||||
g_clear_pointer(&now, g_date_time_unref);
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean person_entity_service_update_confidence(Database *database,
|
||||
const char *entity_identifier, gint confidence, GError **error)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
/**
|
||||
* @brief Version actuelle du schéma SQLite.
|
||||
*/
|
||||
#define DATABASE_SCHEMA_VERSION_CURRENT 5
|
||||
#define DATABASE_SCHEMA_VERSION_CURRENT 6
|
||||
|
||||
/**
|
||||
* @brief Version actuelle sous forme textuelle pour metadata.
|
||||
*/
|
||||
#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "5"
|
||||
#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "6"
|
||||
|
||||
/**
|
||||
* @brief Nom de l'application enregistré dans les métadonnées.
|
||||
|
|
@ -730,6 +730,24 @@ rollback:
|
|||
return false;
|
||||
}
|
||||
|
||||
/** @brief Applique atomiquement la migration du schéma V5 vers V6. */
|
||||
static bool database_migrate_v5_to_v6(Database *database)
|
||||
{
|
||||
bool transaction_started = false;
|
||||
if (database == NULL || !database_transaction_begin(database))
|
||||
return false;
|
||||
transaction_started = true;
|
||||
if (!schema_install_v6(database) ||
|
||||
!database_update_schema_version(database, "6") ||
|
||||
!database_transaction_commit(database))
|
||||
goto rollback;
|
||||
return true;
|
||||
rollback:
|
||||
if (transaction_started && !database_transaction_rollback(database))
|
||||
g_warning("Impossible d’annuler la migration SQLite V5 vers V6.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Garantit atomiquement la présence des extensions du schéma courant.
|
||||
*/
|
||||
|
|
@ -934,6 +952,12 @@ bool database_migrate_to_latest(
|
|||
schema_version = 5;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (!database_migrate_v5_to_v6(database))
|
||||
return false;
|
||||
schema_version = 6;
|
||||
break;
|
||||
|
||||
default:
|
||||
database_set_error(
|
||||
database,
|
||||
|
|
@ -1060,6 +1084,11 @@ bool database_initialize(
|
|||
goto rollback;
|
||||
}
|
||||
|
||||
if (!schema_install_v6(database))
|
||||
{
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
if (!schema_ensure_current(
|
||||
database
|
||||
))
|
||||
|
|
|
|||
|
|
@ -221,6 +221,12 @@ bool schema_install_v5(Database *database)
|
|||
"la migration SQLite V5");
|
||||
}
|
||||
|
||||
bool schema_install_v6(Database *database)
|
||||
{
|
||||
return schema_execute_file(database, "database/schema_v6.sql",
|
||||
"la migration SQLite V6");
|
||||
}
|
||||
|
||||
bool schema_ensure_current(
|
||||
Database *database
|
||||
)
|
||||
|
|
|
|||
|
|
@ -613,6 +613,8 @@ const char *person_role_to_code(PersonRole role)
|
|||
case PERSON_ROLE_WITNESS: return "witness";
|
||||
case PERSON_ROLE_SUSPECT: return "suspect";
|
||||
case PERSON_ROLE_RELATED_PERSON: return "related_person";
|
||||
case PERSON_ROLE_IMPERSONATED_IDENTITY:
|
||||
return "impersonated_identity";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -626,6 +628,8 @@ PersonRole person_role_from_code(const char *code)
|
|||
if (g_strcmp0(code, "suspect") == 0) return PERSON_ROLE_SUSPECT;
|
||||
if (g_strcmp0(code, "related_person") == 0)
|
||||
return PERSON_ROLE_RELATED_PERSON;
|
||||
if (g_strcmp0(code, "impersonated_identity") == 0)
|
||||
return PERSON_ROLE_IMPERSONATED_IDENTITY;
|
||||
return PERSON_ROLE_UNCATEGORIZED;
|
||||
}
|
||||
|
||||
|
|
@ -638,6 +642,7 @@ const char *person_role_get_label(PersonRole role)
|
|||
case PERSON_ROLE_WITNESS: return "Témoin";
|
||||
case PERSON_ROLE_SUSPECT: return "Suspect";
|
||||
case PERSON_ROLE_RELATED_PERSON: return "Personne liée";
|
||||
case PERSON_ROLE_IMPERSONATED_IDENTITY: return "Identité usurpée";
|
||||
case PERSON_ROLE_UNCATEGORIZED:
|
||||
default: return "Non catégorisé";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ struct MainWindow
|
|||
gpointer person_role_user_data;
|
||||
MainWindowPersonConfidenceCallback person_confidence_callback;
|
||||
gpointer person_confidence_user_data;
|
||||
MainWindowPersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
|
||||
MainWindowOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
|
@ -551,6 +553,16 @@ static void main_window_on_person_confidence_changed(
|
|||
main_window->person_confidence_user_data);
|
||||
}
|
||||
|
||||
/** @brief Relaie la modification du nom affiché d'une personne. */
|
||||
static void main_window_on_person_name_changed(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data)
|
||||
{
|
||||
MainWindow *main_window = user_data;
|
||||
if (main_window != NULL && main_window->person_name_callback != NULL)
|
||||
main_window->person_name_callback(entity_identifier, display_name,
|
||||
main_window->person_name_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Relaie la demande de vérification provenant du Workspace.
|
||||
*/
|
||||
|
|
@ -895,6 +907,8 @@ MainWindow *main_window_new(
|
|||
main_window_on_person_role_changed, main_window);
|
||||
workspace_set_person_confidence_callback(main_window->workspace,
|
||||
main_window_on_person_confidence_changed, main_window);
|
||||
workspace_set_person_name_callback(main_window->workspace,
|
||||
main_window_on_person_name_changed, main_window);
|
||||
|
||||
workspace_set_osint_action_callback(
|
||||
main_window->workspace,
|
||||
|
|
@ -1718,6 +1732,14 @@ void main_window_set_person_confidence_callback(MainWindow *main_window,
|
|||
main_window->person_confidence_user_data = user_data;
|
||||
}
|
||||
|
||||
void main_window_set_person_name_callback(MainWindow *main_window,
|
||||
MainWindowPersonNameCallback callback, gpointer user_data)
|
||||
{
|
||||
if (main_window == NULL) return;
|
||||
main_window->person_name_callback = callback;
|
||||
main_window->person_name_user_data = user_data;
|
||||
}
|
||||
|
||||
void main_window_set_quit_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowQuitCallback callback,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ struct EntityDetailsPanel
|
|||
GtkDropDown *person_role_dropdown;
|
||||
GtkSpinButton *person_confidence_spin;
|
||||
GtkWidget *person_confidence_button;
|
||||
GtkEntry *person_name_entry;
|
||||
GtkWidget *person_name_button;
|
||||
|
||||
char *selected_entity_identifier;
|
||||
|
||||
|
|
@ -50,8 +52,26 @@ struct EntityDetailsPanel
|
|||
gboolean updating_person_role;
|
||||
EntityDetailsPanelPersonConfidenceCallback person_confidence_callback;
|
||||
gpointer person_confidence_user_data;
|
||||
EntityDetailsPanelPersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
};
|
||||
|
||||
/** @brief Relaie le nouveau nom affiché explicitement enregistré. */
|
||||
static void entity_details_panel_on_person_name_clicked(GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
EntityDetailsPanel *details_panel = user_data;
|
||||
const char *name = NULL;
|
||||
(void) button;
|
||||
if (details_panel == NULL || details_panel->person_name_callback == NULL ||
|
||||
details_panel->selected_entity_identifier == NULL) return;
|
||||
name = gtk_editable_get_text(GTK_EDITABLE(details_panel->person_name_entry));
|
||||
if (name == NULL || name[0] == '\0') return;
|
||||
details_panel->person_name_callback(
|
||||
details_panel->selected_entity_identifier, name,
|
||||
details_panel->person_name_user_data);
|
||||
}
|
||||
|
||||
/** @brief Relaie la confiance explicitement enregistrée par l'utilisateur. */
|
||||
static void entity_details_panel_on_person_confidence_clicked(
|
||||
GtkButton *button, gpointer user_data)
|
||||
|
|
@ -80,7 +100,7 @@ static void entity_details_panel_on_person_role_changed(GObject *object,
|
|||
details_panel->selected_entity_identifier == NULL)
|
||||
return;
|
||||
selected = gtk_drop_down_get_selected(details_panel->person_role_dropdown);
|
||||
if (selected > PERSON_ROLE_RELATED_PERSON) return;
|
||||
if (selected > PERSON_ROLE_IMPERSONATED_IDENTITY) return;
|
||||
details_panel->person_role_callback(
|
||||
details_panel->selected_entity_identifier, (PersonRole) selected,
|
||||
details_panel->person_role_user_data);
|
||||
|
|
@ -744,7 +764,7 @@ EntityDetailsPanel *entity_details_panel_new(void)
|
|||
details_panel->person_role_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
|
||||
person_role_labels = gtk_string_list_new(NULL);
|
||||
for (guint role = PERSON_ROLE_UNCATEGORIZED;
|
||||
role <= PERSON_ROLE_RELATED_PERSON; role++)
|
||||
role <= PERSON_ROLE_IMPERSONATED_IDENTITY; role++)
|
||||
gtk_string_list_append(person_role_labels,
|
||||
person_role_get_label((PersonRole) role));
|
||||
details_panel->person_role_dropdown = GTK_DROP_DOWN(
|
||||
|
|
@ -764,12 +784,25 @@ EntityDetailsPanel *entity_details_panel_new(void)
|
|||
GTK_WIDGET(details_panel->person_confidence_spin));
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
details_panel->person_confidence_button);
|
||||
details_panel->person_name_entry = GTK_ENTRY(gtk_entry_new());
|
||||
gtk_entry_set_placeholder_text(details_panel->person_name_entry,
|
||||
"Nom réel ou nom affiché");
|
||||
details_panel->person_name_button = gtk_button_new_with_label(
|
||||
"Enregistrer le nom");
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
gtk_label_new("Nom affiché dans le graphe"));
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
GTK_WIDGET(details_panel->person_name_entry));
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
details_panel->person_name_button);
|
||||
gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box);
|
||||
g_signal_connect(details_panel->person_role_dropdown, "notify::selected",
|
||||
G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel);
|
||||
g_signal_connect(details_panel->person_confidence_button, "clicked",
|
||||
G_CALLBACK(entity_details_panel_on_person_confidence_clicked),
|
||||
details_panel);
|
||||
g_signal_connect(details_panel->person_name_button, "clicked",
|
||||
G_CALLBACK(entity_details_panel_on_person_name_clicked), details_panel);
|
||||
|
||||
if (details_panel->entity_value_label == NULL ||
|
||||
details_panel->entity_type_label == NULL ||
|
||||
|
|
@ -896,6 +929,9 @@ void entity_details_panel_set_entity(
|
|||
(guint) entity_record_get_person_role(entity_record));
|
||||
gtk_spin_button_set_value(details_panel->person_confidence_spin,
|
||||
entity_record_get_confidence(entity_record));
|
||||
gtk_editable_set_text(GTK_EDITABLE(details_panel->person_name_entry),
|
||||
entity_record_get_label(entity_record) != NULL
|
||||
? entity_record_get_label(entity_record) : "");
|
||||
details_panel->updating_person_role = FALSE;
|
||||
|
||||
entity_details_panel_update_add_relation_button(
|
||||
|
|
@ -1161,6 +1197,15 @@ void entity_details_panel_set_person_confidence_callback(
|
|||
details_panel->person_confidence_user_data = user_data;
|
||||
}
|
||||
|
||||
void entity_details_panel_set_person_name_callback(
|
||||
EntityDetailsPanel *details_panel,
|
||||
EntityDetailsPanelPersonNameCallback callback, gpointer user_data)
|
||||
{
|
||||
if (details_panel == NULL) return;
|
||||
details_panel->person_name_callback = callback;
|
||||
details_panel->person_name_user_data = user_data;
|
||||
}
|
||||
|
||||
gboolean entity_details_panel_is_open(
|
||||
const EntityDetailsPanel *details_panel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3343,6 +3343,8 @@ static void investigation_graph_view_draw_entity(
|
|||
cairo_set_source_rgb(cairo_context, 0.62, 0.32, 0.08); break;
|
||||
case PERSON_ROLE_RELATED_PERSON:
|
||||
cairo_set_source_rgb(cairo_context, 0.38, 0.20, 0.52); break;
|
||||
case PERSON_ROLE_IMPERSONATED_IDENTITY:
|
||||
cairo_set_source_rgb(cairo_context, 0.61, 0.35, 0.71); break;
|
||||
case PERSON_ROLE_UNCATEGORIZED:
|
||||
default:
|
||||
cairo_set_source_rgb(cairo_context, 0.25, 0.27, 0.31); break;
|
||||
|
|
|
|||
|
|
@ -138,6 +138,8 @@ struct Workspace
|
|||
gpointer person_role_user_data;
|
||||
WorkspacePersonConfidenceCallback person_confidence_callback;
|
||||
gpointer person_confidence_user_data;
|
||||
WorkspacePersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
|
||||
WorkspaceOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
|
@ -894,6 +896,16 @@ static void workspace_on_person_confidence_changed(
|
|||
workspace->person_confidence_user_data);
|
||||
}
|
||||
|
||||
/** @brief Relaie la modification du nom affiché d'une personne. */
|
||||
static void workspace_on_person_name_changed(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data)
|
||||
{
|
||||
Workspace *workspace = user_data;
|
||||
if (workspace != NULL && workspace->person_name_callback != NULL)
|
||||
workspace->person_name_callback(entity_identifier, display_name,
|
||||
workspace->person_name_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
|
||||
*/
|
||||
|
|
@ -1816,6 +1828,9 @@ Workspace *workspace_new(void)
|
|||
entity_details_panel_set_person_confidence_callback(
|
||||
workspace->entity_details_panel,
|
||||
workspace_on_person_confidence_changed, workspace);
|
||||
entity_details_panel_set_person_name_callback(
|
||||
workspace->entity_details_panel, workspace_on_person_name_changed,
|
||||
workspace);
|
||||
|
||||
gtk_overlay_set_child(
|
||||
GTK_OVERLAY(
|
||||
|
|
@ -3281,6 +3296,14 @@ void workspace_set_person_confidence_callback(Workspace *workspace,
|
|||
workspace->person_confidence_user_data = user_data;
|
||||
}
|
||||
|
||||
void workspace_set_person_name_callback(Workspace *workspace,
|
||||
WorkspacePersonNameCallback callback, gpointer user_data)
|
||||
{
|
||||
if (workspace == NULL) return;
|
||||
workspace->person_name_callback = callback;
|
||||
workspace->person_name_user_data = user_data;
|
||||
}
|
||||
|
||||
void workspace_reset_graph_layout(
|
||||
Workspace *workspace
|
||||
)
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ static void test_database_initialize_valid_database(void)
|
|||
"FROM investigation;"
|
||||
);
|
||||
|
||||
assert(strcmp(schema_version, "5") == 0);
|
||||
assert(strcmp(schema_version, "6") == 0);
|
||||
test_database_assert_table_exists(database, "osint_executions");
|
||||
test_database_assert_table_exists(database, "osint_execution_entities");
|
||||
test_database_assert_table_exists(database, "osint_execution_relations");
|
||||
|
|
@ -989,7 +989,7 @@ static void test_database_migrate_v1_to_v2(void)
|
|||
assert(
|
||||
strcmp(
|
||||
schema_version,
|
||||
"5"
|
||||
"6"
|
||||
) == 0
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,23 @@ static void test_person_entity_create(void)
|
|||
record = entity_dao_find_by_identifier(dao, identifier, &error);
|
||||
assert(record != NULL && error == NULL);
|
||||
assert(entity_record_get_person_role(record) == PERSON_ROLE_VICTIM);
|
||||
entity_record_free(record); record = NULL;
|
||||
assert(person_entity_service_update_role(database, identifier,
|
||||
PERSON_ROLE_IMPERSONATED_IDENTITY, &error));
|
||||
record = entity_dao_find_by_identifier(dao, identifier, &error);
|
||||
assert(record != NULL && error == NULL);
|
||||
assert(entity_record_get_person_role(record) ==
|
||||
PERSON_ROLE_IMPERSONATED_IDENTITY);
|
||||
assert(strcmp(person_role_to_code(PERSON_ROLE_IMPERSONATED_IDENTITY),
|
||||
"impersonated_identity") == 0);
|
||||
assert(strcmp(person_role_get_label(PERSON_ROLE_IMPERSONATED_IDENTITY),
|
||||
"Identité usurpée") == 0);
|
||||
assert(person_entity_service_update_display_name(database, identifier,
|
||||
"Vraie identité", &error));
|
||||
entity_record_free(record); record = NULL;
|
||||
record = entity_dao_find_by_identifier(dao, identifier, &error);
|
||||
assert(record != NULL && error == NULL);
|
||||
assert(strcmp(entity_record_get_label(record), "Vraie identité") == 0);
|
||||
entity_record_free(record); entity_dao_free(dao); database_close(database);
|
||||
assert(g_remove(path) == 0); assert(g_rmdir(directory) == 0);
|
||||
g_free(identifier); g_free(path); g_free(directory);
|
||||
|
|
|
|||
Loading…
Reference in a new issue