Sleuth Kit Java Bindings (JNI) 4.14.0
Java bindings for using The Sleuth Kit
Loading...
Searching...
No Matches
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 */
19package org.sleuthkit.datamodel;
20
21import com.google.common.base.Strings;
22import java.sql.PreparedStatement;
23import java.sql.ResultSet;
24import java.sql.SQLException;
25import java.sql.Statement;
26import java.util.ArrayList;
27import java.util.Collections;
28import java.util.List;
29import java.util.Optional;
30import java.util.stream.Collectors;
31import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
32import org.sleuthkit.datamodel.TskEvent.PersonsAddedTskEvent;
33
37public final class PersonManager {
38
39 private final SleuthkitCase db;
40
47 PersonManager(SleuthkitCase skCase) {
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<>();
62 db.acquireSingleUserCaseReadLock();
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 {
75 db.releaseSingleUserCaseReadLock();
76 }
77 }
78
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();
97 db.acquireSingleUserCaseWriteLock();
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 {
106 db.releaseSingleUserCaseWriteLock();
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;
125 db.acquireSingleUserCaseWriteLock();
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 {
140 db.releaseSingleUserCaseWriteLock();
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 {
159 db.acquireSingleUserCaseReadLock();
160 try (CaseDbConnection connection = this.db.getConnection()) {
161 return getPerson(name, connection);
162 } finally {
163 db.releaseSingleUserCaseReadLock();
164 }
165 }
166
177 public Optional<Person> getPerson(long id) throws TskCoreException {
178 String queryString = "SELECT * FROM tsk_persons WHERE id = " + id;
179 db.acquireSingleUserCaseReadLock();
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 {
192 db.releaseSingleUserCaseReadLock();
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;
216 db.acquireSingleUserCaseWriteLock();
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 {
253 db.releaseSingleUserCaseWriteLock();
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
364 db.acquireSingleUserCaseReadLock();
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 {
377 db.releaseSingleUserCaseReadLock();
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(long id)
void addHostsToPerson(Person person, List< Host > hosts)
Optional< Person > getPerson(String name)
List< Host > getHostsForPerson(Person person)
void removeHostsFromPerson(Person person, List< Host > hosts)
Optional< Person > getPerson(Host host)
static HostDbStatus fromID(int typeId)
Definition Host.java:125

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