Sleuth Kit Java Bindings (JNI)  4.11.0
Java bindings for using The Sleuth Kit
PersonManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
21 import com.google.common.base.Strings;
22 import java.sql.PreparedStatement;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.sql.Statement;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.stream.Collectors;
33 
37 public final class PersonManager {
38 
39  private final SleuthkitCase db;
40 
48  this.db = skCase;
49  }
50 
58  public List<Person> getPersons() throws TskCoreException {
59  String queryString = "SELECT * FROM tsk_persons";
60 
61  List<Person> persons = new ArrayList<>();
63  try (CaseDbConnection connection = this.db.getConnection();
64  Statement s = connection.createStatement();
65  ResultSet rs = connection.executeQuery(s, queryString)) {
66 
67  while (rs.next()) {
68  persons.add(new Person(rs.getLong("id"), rs.getString("name")));
69  }
70 
71  return persons;
72  } catch (SQLException ex) {
73  throw new TskCoreException(String.format("Error getting persons"), ex);
74  } finally {
76  }
77  }
78 
88  public Person updatePerson(Person person) throws TskCoreException {
89 
90  // Must have a non-empty name
91  if (Strings.isNullOrEmpty(person.getName())) {
92  throw new TskCoreException("Illegal argument passed to updatePerson: Name field for person with ID " + person.getPersonId() + " is null/empty. Will not update database.");
93  }
94 
95  String queryString = "UPDATE tsk_persons"
96  + " SET name = ? WHERE id = " + person.getPersonId();
98  try (CaseDbConnection connection = db.getConnection()) {
99  PreparedStatement s = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
100  s.clearParameters();
101  s.setString(1, person.getName());
102  s.executeUpdate();
103  } catch (SQLException ex) {
104  throw new TskCoreException(String.format("Error updating person with id = %d", person.getPersonId()), ex);
105  } finally {
107  }
108 
109  db.fireTSKEvent(new TskEvent.PersonsUpdatedTskEvent(Collections.singletonList(person)));
110  return person;
111  }
112 
120  public void deletePerson(String name) throws TskCoreException {
121  String queryString = "DELETE FROM tsk_persons"
122  + " WHERE LOWER(name) = LOWER(?)";
123 
124  Person deletedPerson = null;
126  try (CaseDbConnection connection = db.getConnection()) {
127  PreparedStatement s = connection.getPreparedStatement(queryString, Statement.RETURN_GENERATED_KEYS);
128  s.clearParameters();
129  s.setString(1, name);
130  s.executeUpdate();
131 
132  try (ResultSet resultSet = s.getGeneratedKeys()) {
133  if (resultSet.next()) {
134  deletedPerson = new Person(resultSet.getLong(1), name);
135  }
136  }
137  } catch (SQLException ex) {
138  throw new TskCoreException(String.format("Error deleting person with name %s", name), ex);
139  } finally {
141  }
142 
143  if (deletedPerson != null) {
144  db.fireTSKEvent(new TskEvent.PersonsDeletedTskEvent(Collections.singletonList(deletedPerson.getPersonId())));
145  }
146  }
147 
158  public Optional<Person> getPerson(String name) throws TskCoreException {
160  try (CaseDbConnection connection = this.db.getConnection()) {
161  return getPerson(name, connection);
162  } finally {
164  }
165  }
166 
177  public Optional<Person> getPerson(long id) throws TskCoreException {
178  String queryString = "SELECT * FROM tsk_persons WHERE id = " + id;
180  try (CaseDbConnection connection = this.db.getConnection();
181  Statement s = connection.createStatement();
182  ResultSet rs = connection.executeQuery(s, queryString)) {
183 
184  if (rs.next()) {
185  return Optional.of(new Person(rs.getLong("id"), rs.getString("name")));
186  } else {
187  return Optional.empty();
188  }
189  } catch (SQLException ex) {
190  throw new TskCoreException(String.format("Error getting persons"), ex);
191  } finally {
193  }
194  }
195 
207  public Person newPerson(String name) throws TskCoreException {
208 
209  // Must have a name
210  if (Strings.isNullOrEmpty(name)) {
211  throw new TskCoreException("Illegal argument passed to createPerson: Non-empty name is required.");
212  }
213 
214  Person toReturn = null;
215  CaseDbConnection connection = null;
217  try {
218  connection = db.getConnection();
219 
220  // First try to load it from the database. This is a case-insensitive look-up
221  // to attempt to prevent having two entries with the same lower-case name.
222  Optional<Person> person = getPerson(name, connection);
223  if (person.isPresent()) {
224  return person.get();
225  }
226 
227  // Attempt to insert the new Person.
228  String personInsertSQL = "INSERT INTO tsk_persons(name) VALUES (?)"; // NON-NLS
229  PreparedStatement preparedStatement = connection.getPreparedStatement(personInsertSQL, Statement.RETURN_GENERATED_KEYS);
230  preparedStatement.clearParameters();
231  preparedStatement.setString(1, name);
232  connection.executeUpdate(preparedStatement);
233 
234  // Read back the row id.
235  try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
236  if (resultSet.next()) {
237  toReturn = new Person(resultSet.getLong(1), name); //last_insert_rowid()
238  } else {
239  throw new SQLException("Error executing SQL: " + personInsertSQL);
240  }
241  }
242  } catch (SQLException ex) {
243  if (connection != null) {
244  // The insert may have failed because this person was just added on another thread, so try getting the person again.
245  // (Note: the SingleUserCaseWriteLock is a no-op for multi-user cases so acquiring it does not prevent this situation)
246  Optional<Person> person = getPerson(name, connection);
247  if (person.isPresent()) {
248  return person.get();
249  }
250  }
251  throw new TskCoreException(String.format("Error adding person with name = %s", name), ex);
252  } finally {
254  }
255 
256  if (toReturn != null) {
257  db.fireTSKEvent(new PersonsAddedTskEvent(Collections.singletonList(toReturn)));
258  }
259  return toReturn;
260  }
261 
272  public List<Host> getHostsForPerson(Person person) throws TskCoreException {
273  return executeHostsQuery("SELECT * FROM tsk_hosts WHERE person_id = " + person.getPersonId());
274  }
275 
284  public List<Host> getHostsWithoutPersons() throws TskCoreException {
285  return executeHostsQuery("SELECT * FROM tsk_hosts WHERE person_id IS NULL");
286  }
287 
298  private List<Host> executeHostsQuery(String hostsQuery) throws TskCoreException {
299  String sql = hostsQuery + " AND db_status = " + Host.HostDbStatus.ACTIVE.getId();
300  List<Host> hosts = new ArrayList<>();
302  try (CaseDbConnection connection = this.db.getConnection();
303  Statement s = connection.createStatement();
304  ResultSet rs = connection.executeQuery(s, sql)) {
305  while (rs.next()) {
306  hosts.add(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
307  }
308  return hosts;
309  } catch (SQLException ex) {
310  throw new TskCoreException(String.format("Error executing '" + sql + "'"), ex);
311  } finally {
313  }
314  }
315 
327  private Optional<Person> getPerson(String name, CaseDbConnection connection) throws TskCoreException {
328 
329  String queryString = "SELECT * FROM tsk_persons"
330  + " WHERE LOWER(name) = LOWER(?)";
331  try {
332  PreparedStatement s = connection.getPreparedStatement(queryString, Statement.RETURN_GENERATED_KEYS);
333  s.clearParameters();
334  s.setString(1, name);
335 
336  try (ResultSet rs = s.executeQuery()) {
337  if (!rs.next()) {
338  return Optional.empty(); // no match found
339  } else {
340  return Optional.of(new Person(rs.getLong("id"), rs.getString("name")));
341  }
342  }
343  } catch (SQLException ex) {
344  throw new TskCoreException(String.format("Error getting person with name = %s", name), ex);
345  }
346  }
347 
357  public Optional<Person> getPerson(Host host) throws TskCoreException {
358 
359  String queryString = "SELECT p.id AS personId, p.name AS name FROM \n"
360  + "tsk_persons p INNER JOIN tsk_hosts h\n"
361  + "ON p.id = h.person_id \n"
362  + "WHERE h.id = " + host.getHostId();
363 
365  try (CaseDbConnection connection = this.db.getConnection();
366  Statement s = connection.createStatement();
367  ResultSet rs = connection.executeQuery(s, queryString)) {
368 
369  if (rs.next()) {
370  return Optional.of(new Person(rs.getLong("personId"), rs.getString("name")));
371  } else {
372  return Optional.empty();
373  }
374  } catch (SQLException ex) {
375  throw new TskCoreException(String.format("Error getting person for host with ID = %d", host.getHostId()), ex);
376  } finally {
378  }
379  }
380 
389  public void addHostsToPerson(Person person, List<Host> hosts) throws TskCoreException {
390  if (person == null) {
391  throw new TskCoreException("Illegal argument: person must be non-null");
392  }
393  if (hosts == null || hosts.isEmpty()) {
394  throw new TskCoreException("Illegal argument: hosts must be non-null and non-empty");
395  }
396  executeHostsUpdate(person, getHostIds(hosts), new TskEvent.HostsAddedToPersonTskEvent(person, hosts));
397  }
398 
407  public void removeHostsFromPerson(Person person, List<Host> hosts) throws TskCoreException {
408  if (person == null) {
409  throw new TskCoreException("Illegal argument: person must be non-null");
410  }
411  if (hosts == null || hosts.isEmpty()) {
412  throw new TskCoreException("Illegal argument: hosts must be non-null and non-empty");
413  }
414  List<Long> hostIds = getHostIds(hosts);
415  executeHostsUpdate(null, hostIds, new TskEvent.HostsRemovedFromPersonTskEvent(person, hostIds));
416  }
417 
429  private void executeHostsUpdate(Person person, List<Long> hostIds, TskEvent event) throws TskCoreException {
430  String updateSql = null;
432  try (CaseDbConnection connection = this.db.getConnection(); Statement statement = connection.createStatement()) {
433  updateSql = (person == null)
434  ? String.format("UPDATE tsk_hosts SET person_id = NULL")
435  : String.format("UPDATE tsk_hosts SET person_id = %d", person.getPersonId());
436  String hostIdsCsvList = hostIds.stream()
437  .map(hostId -> hostId.toString())
438  .collect(Collectors.joining(","));
439  updateSql += " WHERE id IN (" + hostIdsCsvList + ")";
440  statement.executeUpdate(updateSql);
441  db.fireTSKEvent(event);
442  } catch (SQLException ex) {
443  throw new TskCoreException(String.format(updateSql == null ? "Error connecting to case database" : "Error executing '" + updateSql + "'"), ex);
444  } finally {
446  }
447  }
448 
456  private List<Long> getHostIds(List<Host> hosts) {
457  List<Long> hostIds = new ArrayList<>();
458  hostIds.addAll(hosts.stream()
459  .map(host -> host.getHostId())
460  .collect(Collectors.toList()));
461  return hostIds;
462  }
463 
464 }
Optional< Person > getPerson(String name)
Optional< Person > getPerson(Host host)
void removeHostsFromPerson(Person person, List< Host > hosts)
Optional< Person > getPerson(long id)
void addHostsToPerson(Person person, List< Host > hosts)
List< Host > getHostsForPerson(Person person)

Copyright © 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.