Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
RdbmsCentralRepo.java
Go to the documentation of this file.
1 /*
2  * Central Repository
3  *
4  * Copyright 2015-2020 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.autopsy.centralrepository.datamodel;
20 
21 import com.google.common.cache.Cache;
22 import com.google.common.cache.CacheBuilder;
23 import com.google.common.cache.CacheLoader;
24 import java.net.UnknownHostException;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Collection;
28 import java.util.LinkedHashSet;
29 import java.util.stream.Collectors;
30 import java.sql.Connection;
31 import java.sql.PreparedStatement;
32 import java.sql.ResultSet;
33 import java.sql.SQLException;
34 import java.sql.Statement;
35 import java.sql.Types;
36 import java.time.LocalDate;
37 import java.util.Arrays;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.Set;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.TimeUnit;
43 import java.util.logging.Level;
44 import org.apache.commons.lang3.tuple.Pair;
45 import org.openide.util.NbBundle.Messages;
48 import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil.updateSchemaVersion;
52 import org.sleuthkit.datamodel.Account;
53 import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
54 import org.sleuthkit.datamodel.TskData;
55 
61 abstract class RdbmsCentralRepo implements CentralRepository {
62 
63  private final static Logger logger = Logger.getLogger(RdbmsCentralRepo.class.getName());
64  static final String SCHEMA_MAJOR_VERSION_KEY = "SCHEMA_VERSION";
65  static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION";
66  static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION";
67  static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION";
68  static final CaseDbSchemaVersionNumber SOFTWARE_CR_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 4);
69 
70  protected final List<CorrelationAttributeInstance.Type> defaultCorrelationTypes;
71 
72  private int bulkArtifactsCount;
73  protected int bulkArtifactsThreshold;
74  private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts;
75  private static final int CASE_CACHE_TIMEOUT = 5;
76  private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
77  private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
78  private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
79  private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
80  .expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
81  build();
82 
83  private boolean isCRTypeCacheInitialized;
84  private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build();
85  private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder()
86  .expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
87  build();
88  private static final Cache<Integer, CorrelationCase> caseCacheById = CacheBuilder.newBuilder()
89  .expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
90  build();
91  private static final Cache<String, CorrelationDataSource> dataSourceCacheByDsObjectId = CacheBuilder.newBuilder()
92  .expireAfterWrite(DATA_SOURCE_CACHE_TIMEOUT, TimeUnit.MINUTES).
93  build();
94  private static final Cache<String, CorrelationDataSource> dataSourceCacheById = CacheBuilder.newBuilder()
95  .expireAfterWrite(DATA_SOURCE_CACHE_TIMEOUT, TimeUnit.MINUTES).
96  build();
97  // Maximum length for the value column in the instance tables
98  static final int MAX_VALUE_LENGTH = 256;
99 
100  // number of instances to keep in bulk queue before doing an insert.
101  // Update Test code if this changes. It's hard coded there.
102  static final int DEFAULT_BULK_THRESHHOLD = 1000;
103 
109  protected RdbmsCentralRepo() throws CentralRepoException {
110  isCRTypeCacheInitialized = false;
111  bulkArtifactsCount = 0;
112  bulkArtifacts = new HashMap<>();
113 
114  defaultCorrelationTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes();
115  defaultCorrelationTypes.forEach((type) -> {
116  bulkArtifacts.put(CentralRepoDbUtil.correlationTypeToInstanceTableName(type), new ArrayList<>());
117  });
118  }
119 
123  protected abstract Connection connect(boolean foreignKeys) throws CentralRepoException;
124 
128  protected abstract Connection connect() throws CentralRepoException;
129 
133  protected abstract Connection getEphemeralConnection();
142  @Override
143  public void newDbInfo(String name, String value) throws CentralRepoException {
144  Connection conn = connect();
145 
146  PreparedStatement preparedStatement = null;
147  String sql = "INSERT INTO db_info (name, value) VALUES (?, ?) "
148  + getConflictClause();
149  try {
150  preparedStatement = conn.prepareStatement(sql);
151  preparedStatement.setString(1, name);
152  preparedStatement.setString(2, value);
153  preparedStatement.executeUpdate();
154  } catch (SQLException ex) {
155  throw new CentralRepoException("Error adding new name/value pair to db_info.", ex);
156  } finally {
157  CentralRepoDbUtil.closeStatement(preparedStatement);
158  CentralRepoDbUtil.closeConnection(conn);
159  }
160 
161  }
162 
163  @Override
164  public void addDataSourceObjectId(int rowId, long dataSourceObjectId) throws CentralRepoException {
165  Connection conn = connect();
166  PreparedStatement preparedStatement = null;
167  String sql = "UPDATE data_sources SET datasource_obj_id=? WHERE id=?";
168  try {
169  preparedStatement = conn.prepareStatement(sql);
170  preparedStatement.setLong(1, dataSourceObjectId);
171  preparedStatement.setInt(2, rowId);
172  preparedStatement.executeUpdate();
173  } catch (SQLException ex) {
174  throw new CentralRepoException("Error updating data source object id for data_sources row " + rowId, ex);
175  } finally {
176  CentralRepoDbUtil.closeStatement(preparedStatement);
177  CentralRepoDbUtil.closeConnection(conn);
178  }
179  }
180 
190  @Override
191  public String getDbInfo(String name) throws CentralRepoException {
192  Connection conn = connect();
193 
194  PreparedStatement preparedStatement = null;
195  ResultSet resultSet = null;
196  String value = null;
197  String sql = "SELECT value FROM db_info WHERE name=?";
198  try {
199  preparedStatement = conn.prepareStatement(sql);
200  preparedStatement.setString(1, name);
201  resultSet = preparedStatement.executeQuery();
202  if (resultSet.next()) {
203  value = resultSet.getString("value");
204  }
205  } catch (SQLException ex) {
206  throw new CentralRepoException("Error getting value for name.", ex);
207  } finally {
208  CentralRepoDbUtil.closeStatement(preparedStatement);
209  CentralRepoDbUtil.closeResultSet(resultSet);
210  CentralRepoDbUtil.closeConnection(conn);
211  }
212 
213  return value;
214  }
215 
219  protected final void clearCaches() {
220  synchronized(typeCache) {
221  typeCache.invalidateAll();
222  isCRTypeCacheInitialized = false;
223  }
224  caseCacheByUUID.invalidateAll();
225  caseCacheById.invalidateAll();
226  dataSourceCacheByDsObjectId.invalidateAll();
227  dataSourceCacheById.invalidateAll();
228  }
229 
238  @Override
239  public void updateDbInfo(String name, String value) throws CentralRepoException {
240  Connection conn = connect();
241 
242  PreparedStatement preparedStatement = null;
243  String sql = "UPDATE db_info SET value=? WHERE name=?";
244  try {
245  preparedStatement = conn.prepareStatement(sql);
246  preparedStatement.setString(1, value);
247  preparedStatement.setString(2, name);
248  preparedStatement.executeUpdate();
249  } catch (SQLException ex) {
250  throw new CentralRepoException("Error updating value for name.", ex);
251  } finally {
252  CentralRepoDbUtil.closeStatement(preparedStatement);
253  CentralRepoDbUtil.closeConnection(conn);
254  }
255  }
256 
266  @Override
267  public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws CentralRepoException {
268 
269  if (eamCase.getCaseUUID() == null) {
270  throw new CentralRepoException("Case UUID is null");
271  }
272 
273  // check if there is already an existing CorrelationCase for this Case
274  CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID());
275  if (cRCase != null) {
276  return cRCase;
277  }
278 
279  Connection conn = connect();
280  PreparedStatement preparedStatement = null;
281 
282  String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, "
283  + "examiner_name, examiner_email, examiner_phone, notes) "
284  + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) "
285  + getConflictClause();
286  ResultSet resultSet = null;
287  try {
288  preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
289 
290  preparedStatement.setString(1, eamCase.getCaseUUID());
291  if (null == eamCase.getOrg()) {
292  preparedStatement.setNull(2, Types.INTEGER);
293  } else {
294  preparedStatement.setInt(2, eamCase.getOrg().getOrgID());
295  }
296  preparedStatement.setString(3, eamCase.getDisplayName());
297  preparedStatement.setString(4, eamCase.getCreationDate());
298  if ("".equals(eamCase.getCaseNumber())) {
299  preparedStatement.setNull(5, Types.INTEGER);
300  } else {
301  preparedStatement.setString(5, eamCase.getCaseNumber());
302  }
303  if ("".equals(eamCase.getExaminerName())) {
304  preparedStatement.setNull(6, Types.INTEGER);
305  } else {
306  preparedStatement.setString(6, eamCase.getExaminerName());
307  }
308  if ("".equals(eamCase.getExaminerEmail())) {
309  preparedStatement.setNull(7, Types.INTEGER);
310  } else {
311  preparedStatement.setString(7, eamCase.getExaminerEmail());
312  }
313  if ("".equals(eamCase.getExaminerPhone())) {
314  preparedStatement.setNull(8, Types.INTEGER);
315  } else {
316  preparedStatement.setString(8, eamCase.getExaminerPhone());
317  }
318  if ("".equals(eamCase.getNotes())) {
319  preparedStatement.setNull(9, Types.INTEGER);
320  } else {
321  preparedStatement.setString(9, eamCase.getNotes());
322  }
323 
324  preparedStatement.executeUpdate();
325  //update the case in the caches
326  resultSet = preparedStatement.getGeneratedKeys();
327  if (!resultSet.next()) {
328  throw new CentralRepoException(String.format("Failed to INSERT case %s in central repo", eamCase.getCaseUUID()));
329  }
330  int caseID = resultSet.getInt(1); //last_insert_rowid()
331  CorrelationCase correlationCase = new CorrelationCase(caseID, eamCase.getCaseUUID(), eamCase.getOrg(),
332  eamCase.getDisplayName(), eamCase.getCreationDate(), eamCase.getCaseNumber(), eamCase.getExaminerName(),
333  eamCase.getExaminerEmail(), eamCase.getExaminerPhone(), eamCase.getNotes());
334  caseCacheByUUID.put(eamCase.getCaseUUID(), correlationCase);
335  caseCacheById.put(caseID, correlationCase);
336  } catch (SQLException ex) {
337  throw new CentralRepoException("Error inserting new case.", ex); // NON-NLS
338  } finally {
339  CentralRepoDbUtil.closeResultSet(resultSet);
340  CentralRepoDbUtil.closeStatement(preparedStatement);
341  CentralRepoDbUtil.closeConnection(conn);
342  }
343 
344  // get a new version with the updated ID
345  return getCaseByUUID(eamCase.getCaseUUID());
346  }
347 
353  @Override
354  public CorrelationCase newCase(Case autopsyCase) throws CentralRepoException {
355  if (autopsyCase == null) {
356  throw new CentralRepoException("Case is null");
357  }
358 
359  CorrelationCase curCeCase = new CorrelationCase(
360  -1,
361  autopsyCase.getName(), // unique case ID
362  CentralRepoOrganization.getDefault(),
363  autopsyCase.getDisplayName(),
364  autopsyCase.getCreatedDate(),
365  autopsyCase.getNumber(),
366  autopsyCase.getExaminer(),
367  autopsyCase.getExaminerEmail(),
368  autopsyCase.getExaminerPhone(),
369  autopsyCase.getCaseNotes());
370  return newCase(curCeCase);
371  }
372 
373  @Override
374  public CorrelationCase getCase(Case autopsyCase) throws CentralRepoException {
375  return getCaseByUUID(autopsyCase.getName());
376  }
377 
383  @Override
384  public void updateCase(CorrelationCase eamCase) throws CentralRepoException {
385  if (eamCase == null) {
386  throw new CentralRepoException("Correlation case is null");
387  }
388 
389  Connection conn = connect();
390 
391  PreparedStatement preparedStatement = null;
392  String sql = "UPDATE cases "
393  + "SET org_id=?, case_name=?, creation_date=?, case_number=?, examiner_name=?, examiner_email=?, examiner_phone=?, notes=? "
394  + "WHERE case_uid=?";
395 
396  try {
397  preparedStatement = conn.prepareStatement(sql);
398 
399  if (null == eamCase.getOrg()) {
400  preparedStatement.setNull(1, Types.INTEGER);
401  } else {
402  preparedStatement.setInt(1, eamCase.getOrg().getOrgID());
403  }
404  preparedStatement.setString(2, eamCase.getDisplayName());
405  preparedStatement.setString(3, eamCase.getCreationDate());
406 
407  if ("".equals(eamCase.getCaseNumber())) {
408  preparedStatement.setNull(4, Types.INTEGER);
409  } else {
410  preparedStatement.setString(4, eamCase.getCaseNumber());
411  }
412  if ("".equals(eamCase.getExaminerName())) {
413  preparedStatement.setNull(5, Types.INTEGER);
414  } else {
415  preparedStatement.setString(5, eamCase.getExaminerName());
416  }
417  if ("".equals(eamCase.getExaminerEmail())) {
418  preparedStatement.setNull(6, Types.INTEGER);
419  } else {
420  preparedStatement.setString(6, eamCase.getExaminerEmail());
421  }
422  if ("".equals(eamCase.getExaminerPhone())) {
423  preparedStatement.setNull(7, Types.INTEGER);
424  } else {
425  preparedStatement.setString(7, eamCase.getExaminerPhone());
426  }
427  if ("".equals(eamCase.getNotes())) {
428  preparedStatement.setNull(8, Types.INTEGER);
429  } else {
430  preparedStatement.setString(8, eamCase.getNotes());
431  }
432 
433  preparedStatement.setString(9, eamCase.getCaseUUID());
434 
435  preparedStatement.executeUpdate();
436  //update the case in the cache
437  caseCacheById.put(eamCase.getID(), eamCase);
438  caseCacheByUUID.put(eamCase.getCaseUUID(), eamCase);
439  } catch (SQLException ex) {
440  throw new CentralRepoException("Error updating case.", ex); // NON-NLS
441  } finally {
442  CentralRepoDbUtil.closeStatement(preparedStatement);
443  CentralRepoDbUtil.closeConnection(conn);
444  }
445  }
446 
454  @Override
455  public CorrelationCase getCaseByUUID(String caseUUID) throws CentralRepoException {
456  try {
457  return caseCacheByUUID.get(caseUUID, () -> getCaseByUUIDFromCr(caseUUID));
458  } catch (CacheLoader.InvalidCacheLoadException ignored) {
459  //lambda valueloader returned a null value and cache can not store null values this is normal if the case does not exist in the central repo yet
460  return null;
461  } catch (ExecutionException ex) {
462  throw new CentralRepoException("Error getting autopsy case from Central repo", ex);
463  }
464  }
465 
473  private CorrelationCase getCaseByUUIDFromCr(String caseUUID) throws CentralRepoException {
474  Connection conn = connect();
475 
476  CorrelationCase eamCaseResult = null;
477  PreparedStatement preparedStatement = null;
478  ResultSet resultSet = null;
479 
480  String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, "
481  + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone "
482  + "FROM cases "
483  + "LEFT JOIN organizations ON cases.org_id=organizations.id "
484  + "WHERE case_uid=?";
485 
486  try {
487  preparedStatement = conn.prepareStatement(sql);
488  preparedStatement.setString(1, caseUUID);
489  resultSet = preparedStatement.executeQuery();
490  if (resultSet.next()) {
491  eamCaseResult = getEamCaseFromResultSet(resultSet);
492  }
493  if (eamCaseResult != null) {
494  //Update the version in the other cache
495  caseCacheById.put(eamCaseResult.getID(), eamCaseResult);
496  }
497  } catch (SQLException ex) {
498  throw new CentralRepoException("Error getting case details.", ex); // NON-NLS
499  } finally {
500  CentralRepoDbUtil.closeStatement(preparedStatement);
501  CentralRepoDbUtil.closeResultSet(resultSet);
502  CentralRepoDbUtil.closeConnection(conn);
503  }
504 
505  return eamCaseResult;
506  }
507 
515  @Override
516  public CorrelationCase getCaseById(int caseId) throws CentralRepoException {
517  try {
518  return caseCacheById.get(caseId, () -> getCaseByIdFromCr(caseId));
519  } catch (CacheLoader.InvalidCacheLoadException ignored) {
520  //lambda valueloader returned a null value and cache can not store null values this is normal if the case does not exist in the central repo yet
521  return null;
522  } catch (ExecutionException ex) {
523  throw new CentralRepoException("Error getting autopsy case from Central repo", ex);
524  }
525  }
526 
534  private CorrelationCase getCaseByIdFromCr(int caseId) throws CentralRepoException {
535  Connection conn = connect();
536 
537  CorrelationCase eamCaseResult = null;
538  PreparedStatement preparedStatement = null;
539  ResultSet resultSet = null;
540 
541  String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, "
542  + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone "
543  + "FROM cases "
544  + "LEFT JOIN organizations ON cases.org_id=organizations.id "
545  + "WHERE cases.id=?";
546  try {
547  preparedStatement = conn.prepareStatement(sql);
548  preparedStatement.setInt(1, caseId);
549  resultSet = preparedStatement.executeQuery();
550  if (resultSet.next()) {
551  eamCaseResult = getEamCaseFromResultSet(resultSet);
552  }
553  if (eamCaseResult != null) {
554  //Update the version in the other cache
555  caseCacheByUUID.put(eamCaseResult.getCaseUUID(), eamCaseResult);
556  }
557  } catch (SQLException ex) {
558  throw new CentralRepoException("Error getting case details.", ex); // NON-NLS
559  } finally {
560  CentralRepoDbUtil.closeStatement(preparedStatement);
561  CentralRepoDbUtil.closeResultSet(resultSet);
562  CentralRepoDbUtil.closeConnection(conn);
563  }
564 
565  return eamCaseResult;
566  }
567 
573  @Override
574  public List<CorrelationCase> getCases() throws CentralRepoException {
575  Connection conn = connect();
576 
577  List<CorrelationCase> cases = new ArrayList<>();
578  CorrelationCase eamCaseResult;
579  PreparedStatement preparedStatement = null;
580  ResultSet resultSet = null;
581 
582  String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, "
583  + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone "
584  + "FROM cases "
585  + "LEFT JOIN organizations ON cases.org_id=organizations.id";
586 
587  try {
588  preparedStatement = conn.prepareStatement(sql);
589  resultSet = preparedStatement.executeQuery();
590  while (resultSet.next()) {
591  eamCaseResult = getEamCaseFromResultSet(resultSet);
592  cases.add(eamCaseResult);
593  }
594  } catch (SQLException ex) {
595  throw new CentralRepoException("Error getting all cases.", ex); // NON-NLS
596  } finally {
597  CentralRepoDbUtil.closeStatement(preparedStatement);
598  CentralRepoDbUtil.closeResultSet(resultSet);
599  CentralRepoDbUtil.closeConnection(conn);
600  }
601 
602  return cases;
603  }
604 
615  private static String getDataSourceByDSObjectIdCacheKey(int caseId, Long dataSourceObjectId) {
616  return "Case" + caseId + "DsObjectId" + dataSourceObjectId; //NON-NLS
617  }
618 
628  private static String getDataSourceByIdCacheKey(int caseId, int dataSourceId) {
629  return "Case" + caseId + "Id" + dataSourceId; //NON-NLS
630  }
631 
637  @Override
638  public CorrelationDataSource newDataSource(CorrelationDataSource eamDataSource) throws CentralRepoException {
639  if (eamDataSource.getCaseID() == -1) {
640  throw new CentralRepoException("Case ID is -1");
641  }
642  if (eamDataSource.getDeviceID() == null) {
643  throw new CentralRepoException("Device ID is null");
644  }
645  if (eamDataSource.getName() == null) {
646  throw new CentralRepoException("Name is null");
647  }
648  if (eamDataSource.getID() != -1) {
649  // This data source is already in the central repo
650  return eamDataSource;
651  }
652 
653  Connection conn = connect();
654 
655  PreparedStatement preparedStatement = null;
656  //The conflict clause exists in case multiple nodes are trying to add the data source because it did not exist at the same time
657  String sql = "INSERT INTO data_sources(device_id, case_id, name, datasource_obj_id, md5, sha1, sha256) VALUES (?, ?, ?, ?, ?, ?, ?) "
658  + getConflictClause();
659  ResultSet resultSet = null;
660  try {
661  preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
662 
663  preparedStatement.setString(1, eamDataSource.getDeviceID());
664  preparedStatement.setInt(2, eamDataSource.getCaseID());
665  preparedStatement.setString(3, eamDataSource.getName());
666  preparedStatement.setLong(4, eamDataSource.getDataSourceObjectID());
667  preparedStatement.setString(5, eamDataSource.getMd5());
668  preparedStatement.setString(6, eamDataSource.getSha1());
669  preparedStatement.setString(7, eamDataSource.getSha256());
670 
671  preparedStatement.executeUpdate();
672  resultSet = preparedStatement.getGeneratedKeys();
673  if (!resultSet.next()) {
674  /*
675  * If nothing was inserted, then return the data source that
676  * exists in the Central Repository.
677  *
678  * This is expected to occur with PostgreSQL Central Repository
679  * databases.
680  */
681  try {
682  return dataSourceCacheByDsObjectId.get(getDataSourceByDSObjectIdCacheKey(
683  eamDataSource.getCaseID(), eamDataSource.getDataSourceObjectID()),
684  () -> getDataSourceFromCr(eamDataSource.getCaseID(), eamDataSource.getDataSourceObjectID()));
685  } catch (CacheLoader.InvalidCacheLoadException | ExecutionException getException) {
686  throw new CentralRepoException(String.format("Unable to to INSERT or get data source %s in central repo:", eamDataSource.getName()), getException);
687  }
688  } else {
689  //if a new data source was added to the central repository update the caches to include it and return it
690  int dataSourceId = resultSet.getInt(1); //last_insert_rowid()
691  CorrelationDataSource dataSource = new CorrelationDataSource(eamDataSource.getCaseID(), dataSourceId, eamDataSource.getDeviceID(), eamDataSource.getName(), eamDataSource.getDataSourceObjectID(), eamDataSource.getMd5(), eamDataSource.getSha1(), eamDataSource.getSha256());
692  dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(dataSource.getCaseID(), dataSource.getDataSourceObjectID()), dataSource);
693  dataSourceCacheById.put(getDataSourceByIdCacheKey(dataSource.getCaseID(), dataSource.getID()), dataSource);
694  return dataSource;
695  }
696 
697  } catch (SQLException insertException) {
698  /*
699  * If an exception was thrown causing us to not return a new data
700  * source, attempt to get an existing data source with the same case
701  * ID and data source object ID.
702  *
703  * This exception block is expected to occur with SQLite Central
704  * Repository databases.
705  */
706  try {
707  return dataSourceCacheByDsObjectId.get(getDataSourceByDSObjectIdCacheKey(
708  eamDataSource.getCaseID(), eamDataSource.getDataSourceObjectID()),
709  () -> getDataSourceFromCr(eamDataSource.getCaseID(), eamDataSource.getDataSourceObjectID()));
710  } catch (CacheLoader.InvalidCacheLoadException | ExecutionException getException) {
711  throw new CentralRepoException(String.format("Unable to to INSERT or get data source %s in central repo, insert failed due to Exception: %s", eamDataSource.getName(), insertException.getMessage()), getException);
712  }
713  } finally {
714  CentralRepoDbUtil.closeResultSet(resultSet);
715  CentralRepoDbUtil.closeStatement(preparedStatement);
716  CentralRepoDbUtil.closeConnection(conn);
717  }
718  }
719 
731  @Override
732  public CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long dataSourceObjectId) throws CentralRepoException {
733 
734  if (correlationCase == null) {
735  throw new CentralRepoException("Correlation case is null");
736  }
737  try {
738  return dataSourceCacheByDsObjectId.get(getDataSourceByDSObjectIdCacheKey(correlationCase.getID(), dataSourceObjectId), () -> getDataSourceFromCr(correlationCase.getID(), dataSourceObjectId));
739  } catch (CacheLoader.InvalidCacheLoadException ignored) {
740  //lambda valueloader returned a null value and cache can not store null values this is normal if the dataSource does not exist in the central repo yet
741  return null;
742  } catch (ExecutionException ex) {
743  throw new CentralRepoException("Error getting data source from central repository", ex);
744  }
745  }
746 
759  private CorrelationDataSource getDataSourceFromCr(int correlationCaseId, Long dataSourceObjectId) throws CentralRepoException {
760  Connection conn = connect();
761 
762  CorrelationDataSource eamDataSourceResult = null;
763  PreparedStatement preparedStatement = null;
764  ResultSet resultSet = null;
765 
766  String sql = "SELECT * FROM data_sources WHERE datasource_obj_id=? AND case_id=?"; // NON-NLS
767 
768  try {
769  preparedStatement = conn.prepareStatement(sql);
770  preparedStatement.setLong(1, dataSourceObjectId);
771  preparedStatement.setInt(2, correlationCaseId);
772  resultSet = preparedStatement.executeQuery();
773  if (resultSet.next()) {
774  eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
775  }
776  if (eamDataSourceResult != null) {
777  dataSourceCacheById.put(getDataSourceByIdCacheKey(correlationCaseId, eamDataSourceResult.getID()), eamDataSourceResult);
778  }
779  } catch (SQLException ex) {
780  throw new CentralRepoException("Error getting data source.", ex); // NON-NLS
781  } finally {
782  CentralRepoDbUtil.closeStatement(preparedStatement);
783  CentralRepoDbUtil.closeResultSet(resultSet);
784  CentralRepoDbUtil.closeConnection(conn);
785  }
786 
787  return eamDataSourceResult;
788  }
789 
799  @Override
800  public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws CentralRepoException {
801  if (correlationCase == null) {
802  throw new CentralRepoException("Correlation case is null");
803  }
804  try {
805  return dataSourceCacheById.get(getDataSourceByIdCacheKey(correlationCase.getID(), dataSourceId), () -> getDataSourceByIdFromCr(correlationCase, dataSourceId));
806  } catch (CacheLoader.InvalidCacheLoadException ignored) {
807  //lambda valueloader returned a null value and cache can not store null values this is normal if the dataSource does not exist in the central repo yet
808  return null;
809  } catch (ExecutionException ex) {
810  throw new CentralRepoException("Error getting data source from central repository", ex);
811  }
812  }
813 
823  private CorrelationDataSource getDataSourceByIdFromCr(CorrelationCase correlationCase, int dataSourceId) throws CentralRepoException {
824  Connection conn = connect();
825 
826  CorrelationDataSource eamDataSourceResult = null;
827  PreparedStatement preparedStatement = null;
828  ResultSet resultSet = null;
829 
830  String sql = "SELECT * FROM data_sources WHERE id=? AND case_id=?"; // NON-NLS
831 
832  try {
833  preparedStatement = conn.prepareStatement(sql);
834  preparedStatement.setInt(1, dataSourceId);
835  preparedStatement.setInt(2, correlationCase.getID());
836  resultSet = preparedStatement.executeQuery();
837  if (resultSet.next()) {
838  eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
839  }
840  if (eamDataSourceResult != null) {
841  dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(correlationCase.getID(), eamDataSourceResult.getDataSourceObjectID()), eamDataSourceResult);
842  }
843  } catch (SQLException ex) {
844  throw new CentralRepoException("Error getting data source.", ex); // NON-NLS
845  } finally {
846  CentralRepoDbUtil.closeStatement(preparedStatement);
847  CentralRepoDbUtil.closeResultSet(resultSet);
848  CentralRepoDbUtil.closeConnection(conn);
849  }
850 
851  return eamDataSourceResult;
852  }
853 
859  @Override
860  public List<CorrelationDataSource> getDataSources() throws CentralRepoException {
861  Connection conn = connect();
862 
863  List<CorrelationDataSource> dataSources = new ArrayList<>();
864  CorrelationDataSource eamDataSourceResult;
865  PreparedStatement preparedStatement = null;
866  ResultSet resultSet = null;
867 
868  String sql = "SELECT * FROM data_sources";
869 
870  try {
871  preparedStatement = conn.prepareStatement(sql);
872  resultSet = preparedStatement.executeQuery();
873  while (resultSet.next()) {
874  eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
875  dataSources.add(eamDataSourceResult);
876  }
877  } catch (SQLException ex) {
878  throw new CentralRepoException("Error getting all data sources.", ex); // NON-NLS
879  } finally {
880  CentralRepoDbUtil.closeStatement(preparedStatement);
881  CentralRepoDbUtil.closeResultSet(resultSet);
882  CentralRepoDbUtil.closeConnection(conn);
883  }
884 
885  return dataSources;
886  }
887 
893  @Override
894  public void updateDataSourceMd5Hash(CorrelationDataSource eamDataSource) throws CentralRepoException {
895  updateDataSourceStringValue(eamDataSource, "md5", eamDataSource.getMd5());
896  }
897 
903  @Override
904  public void updateDataSourceSha1Hash(CorrelationDataSource eamDataSource) throws CentralRepoException {
905  updateDataSourceStringValue(eamDataSource, "sha1", eamDataSource.getSha1());
906  }
907 
914  @Override
915  public void updateDataSourceSha256Hash(CorrelationDataSource eamDataSource) throws CentralRepoException {
916  updateDataSourceStringValue(eamDataSource, "sha256", eamDataSource.getSha256());
917  }
918 
926  private void updateDataSourceStringValue(CorrelationDataSource eamDataSource, String column, String value) throws CentralRepoException {
927  if (eamDataSource == null) {
928  throw new CentralRepoException("Correlation data source is null");
929  }
930 
931  Connection conn = connect();
932 
933  PreparedStatement preparedStatement = null;
934  String sql = "UPDATE data_sources "
935  + "SET " + column + "=? "
936  + "WHERE id=?";
937 
938  try {
939  preparedStatement = conn.prepareStatement(sql);
940 
941  preparedStatement.setString(1, value);
942  preparedStatement.setInt(2, eamDataSource.getID());
943 
944  preparedStatement.executeUpdate();
945  //update the case in the cache
946  dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(eamDataSource.getCaseID(), eamDataSource.getDataSourceObjectID()), eamDataSource);
947  dataSourceCacheById.put(getDataSourceByIdCacheKey(eamDataSource.getCaseID(), eamDataSource.getID()), eamDataSource);
948  } catch (SQLException ex) {
949  throw new CentralRepoException(String.format("Error updating data source (obj_id=%d).", eamDataSource.getDataSourceObjectID()), ex); // NON-NLS
950  } finally {
951  CentralRepoDbUtil.closeStatement(preparedStatement);
952  CentralRepoDbUtil.closeConnection(conn);
953  }
954  }
955 
964  @Override
965  public void updateDataSourceName(CorrelationDataSource eamDataSource, String newName) throws CentralRepoException {
966 
967  Connection conn = connect();
968 
969  PreparedStatement preparedStatement = null;
970 
971  String sql = "UPDATE data_sources SET name = ? WHERE id = ?";
972 
973  try {
974  preparedStatement = conn.prepareStatement(sql);
975  preparedStatement.setString(1, newName);
976  preparedStatement.setInt(2, eamDataSource.getID());
977  preparedStatement.executeUpdate();
978 
979  CorrelationDataSource updatedDataSource = new CorrelationDataSource(
980  eamDataSource.getCaseID(),
981  eamDataSource.getID(),
982  eamDataSource.getDeviceID(),
983  newName,
984  eamDataSource.getDataSourceObjectID(),
985  eamDataSource.getMd5(),
986  eamDataSource.getSha1(),
987  eamDataSource.getSha256());
988 
989  dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(updatedDataSource.getCaseID(), updatedDataSource.getDataSourceObjectID()), updatedDataSource);
990  dataSourceCacheById.put(getDataSourceByIdCacheKey(updatedDataSource.getCaseID(), updatedDataSource.getID()), updatedDataSource);
991  } catch (SQLException ex) {
992  throw new CentralRepoException("Error updating name of data source with ID " + eamDataSource.getDataSourceObjectID()
993  + " to " + newName, ex); // NON-NLS
994  } finally {
995  CentralRepoDbUtil.closeStatement(preparedStatement);
996  CentralRepoDbUtil.closeConnection(conn);
997  }
998  }
999 
1006  @Override
1007  public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
1008  checkAddArtifactInstanceNulls(eamArtifact);
1009 
1010  // @@@ We should cache the case and data source IDs in memory
1011  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
1012  boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType());
1013 
1014  String sql;
1015  // _instance table for accounts have an additional account_id column
1016  if (artifactHasAnAccount) {
1017  sql = "INSERT INTO "
1018  + tableName
1019  + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
1020  + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
1021  + getConflictClause();
1022  }
1023  else {
1024  sql = "INSERT INTO "
1025  + tableName
1026  + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
1027  + "VALUES (?, ?, ?, ?, ?, ?, ?) "
1028  + getConflictClause();
1029  }
1030 
1031  try (Connection conn = connect();
1032  PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
1033 
1034  if (!eamArtifact.getCorrelationValue().isEmpty()) {
1035  preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
1036  preparedStatement.setInt(2, eamArtifact.getCorrelationDataSource().getID());
1037  preparedStatement.setString(3, eamArtifact.getCorrelationValue());
1038  preparedStatement.setString(4, eamArtifact.getFilePath().toLowerCase());
1039  preparedStatement.setByte(5, eamArtifact.getKnownStatus().getFileKnownValue());
1040 
1041  if ("".equals(eamArtifact.getComment())) {
1042  preparedStatement.setNull(6, Types.INTEGER);
1043  } else {
1044  preparedStatement.setString(6, eamArtifact.getComment());
1045  }
1046  preparedStatement.setLong(7, eamArtifact.getFileObjectId());
1047 
1048  // set in the accountId only for artifacts that represent accounts
1049  if (artifactHasAnAccount) {
1050  if (eamArtifact.getAccountId() >= 0) {
1051  preparedStatement.setLong(8, eamArtifact.getAccountId());
1052  } else {
1053  preparedStatement.setNull(8, Types.INTEGER);
1054  }
1055  }
1056 
1057  preparedStatement.executeUpdate();
1058  }
1059 
1060  } catch (SQLException ex) {
1061  throw new CentralRepoException("Error inserting new artifact into artifacts table.", ex); // NON-NLS
1062  }
1063  }
1064 
1077  @Override
1078  public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
1079  // Get the account fom the accounts table
1080  CentralRepoAccount account = getAccount(crAccountType, accountUniqueID);
1081 
1082  // account not found in the table, create it
1083  if (null == account) {
1084 
1085  String query = "INSERT INTO accounts (account_type_id, account_unique_identifier) "
1086  + "VALUES ( " + crAccountType.getAccountTypeId() + ", '"
1087  + accountUniqueID + "' )";
1088 
1089  try (Connection connection = connect();
1090  Statement s = connection.createStatement();) {
1091 
1092  s.execute(query);
1093  // get the account from the db - should exist now.
1094  account = getAccount(crAccountType, accountUniqueID);
1095  } catch (SQLException ex) {
1096  throw new CentralRepoException("Error adding an account to CR database.", ex);
1097  }
1098  }
1099 
1100  return account;
1101  }
1102 
1103 
1104  @Override
1105  public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
1106  try {
1107  return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
1108  } catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
1109  throw new CentralRepoException("Error looking up CR account type in cache.", ex);
1110  }
1111  }
1112 
1113 
1122  private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
1123 
1124  String sql = "SELECT * FROM account_types WHERE type_name = ?";
1125  try ( Connection conn = connect();
1126  PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
1127 
1128  preparedStatement.setString(1, accountTypeName);
1129  try (ResultSet resultSet = preparedStatement.executeQuery();) {
1130  if (resultSet.next()) {
1131  Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
1132  CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
1133  accountTypesCache.put(accountTypeName, crAccountType);
1134  return crAccountType;
1135  } else {
1136  throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
1137  }
1138  }
1139  } catch (SQLException ex) {
1140  throw new CentralRepoException("Error getting correlation type by id.", ex); // NON-NLS
1141  }
1142  }
1143 
1157  private CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
1158 
1159  CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, accountUniqueID));
1160  if (crAccount == null) {
1161  crAccount = getCRAccountFromDb(crAccountType, accountUniqueID);
1162  if (crAccount != null) {
1163  accountsCache.put(Pair.of(crAccountType, accountUniqueID), crAccount);
1164  }
1165  }
1166 
1167  return crAccount;
1168  }
1169 
1170 
1183  private CentralRepoAccount getCRAccountFromDb(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
1184 
1185  CentralRepoAccount account = null;
1186 
1187  String sql = "SELECT * FROM accounts WHERE account_type_id = ? AND account_unique_identifier = ?";
1188  try ( Connection connection = connect();
1189  PreparedStatement preparedStatement = connection.prepareStatement(sql);) {
1190 
1191  preparedStatement.setInt(1, crAccountType.getAccountTypeId());
1192  preparedStatement.setString(2, accountUniqueID);
1193 
1194  try (ResultSet resultSet = preparedStatement.executeQuery();) {
1195  if (resultSet.next()) {
1196  account = new CentralRepoAccount(resultSet.getInt("id"), crAccountType, resultSet.getString("account_unique_identifier")); //NON-NLS
1197  }
1198  }
1199  } catch (SQLException ex) {
1200  throw new CentralRepoException("Error getting account type id", ex);
1201  }
1202 
1203  return account;
1204  }
1205 
1206 
1207  private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
1208  if (eamArtifact == null) {
1209  throw new CentralRepoException("CorrelationAttribute is null");
1210  }
1211  if (eamArtifact.getCorrelationType() == null) {
1212  throw new CentralRepoException("Correlation type is null");
1213  }
1214  if (eamArtifact.getCorrelationValue() == null) {
1215  throw new CentralRepoException("Correlation value is null");
1216  }
1217  if (eamArtifact.getCorrelationValue().length() >= MAX_VALUE_LENGTH) {
1218  throw new CentralRepoException("Artifact value too long for central repository."
1219  + "\nCorrelationArtifact ID: " + eamArtifact.getID()
1220  + "\nCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
1221  + "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
1222 
1223  }
1224  if (eamArtifact.getCorrelationCase() == null) {
1225  throw new CentralRepoException("CorrelationAttributeInstance case is null");
1226  }
1227  if (eamArtifact.getCorrelationDataSource() == null) {
1228  throw new CentralRepoException("CorrelationAttributeInstance data source is null");
1229  }
1230  if (eamArtifact.getKnownStatus() == null) {
1231  throw new CentralRepoException("CorrelationAttributeInstance known status is null");
1232  }
1233  }
1234 
1235  @Override
1236  public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
1237  if (value == null) {
1238  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances for null value");
1239  }
1240  return getArtifactInstancesByTypeValues(aType, Arrays.asList(value));
1241  }
1242 
1243  @Override
1244  public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValues(CorrelationAttributeInstance.Type aType, List<String> values) throws CentralRepoException, CorrelationAttributeNormalizationException {
1245  if (aType == null) {
1246  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances for null type");
1247  }
1248  if (values == null || values.isEmpty()) {
1249  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances without specified values");
1250  }
1251  return getArtifactInstances(prepareGetInstancesSql(aType, values), aType);
1252  }
1253 
1254  @Override
1255  public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValuesAndCases(CorrelationAttributeInstance.Type aType, List<String> values, List<Integer> caseIds) throws CentralRepoException, CorrelationAttributeNormalizationException {
1256  if (aType == null) {
1257  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances for null type");
1258  }
1259  if (values == null || values.isEmpty()) {
1260  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances without specified values");
1261  }
1262  if (caseIds == null || caseIds.isEmpty()) {
1263  throw new CorrelationAttributeNormalizationException("Cannot get artifact instances without specified cases");
1264  }
1265  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
1266  String sql
1267  = " and "
1268  + tableName
1269  + ".case_id in ('";
1270  StringBuilder inValuesBuilder = new StringBuilder(prepareGetInstancesSql(aType, values));
1271  inValuesBuilder.append(sql);
1272  inValuesBuilder.append(caseIds.stream().map(String::valueOf).collect(Collectors.joining("', '")));
1273  inValuesBuilder.append("')");
1274  return getArtifactInstances(inValuesBuilder.toString(), aType);
1275  }
1276 
1289  private String prepareGetInstancesSql(CorrelationAttributeInstance.Type aType, List<String> values) throws CorrelationAttributeNormalizationException {
1290  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
1291  String sql
1292  = "SELECT "
1293  + tableName
1294  + ".id as instance_id,"
1295  + tableName
1296  + ".value,"
1297  + tableName
1298  + ".file_obj_id,"
1299  + " cases.*, organizations.org_name, organizations.poc_name, organizations.poc_email, organizations.poc_phone, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.datasource_obj_id, data_sources.md5, data_sources.sha1, data_sources.sha256 FROM "
1300  + tableName
1301  + " LEFT JOIN cases ON "
1302  + tableName
1303  + ".case_id=cases.id"
1304  + " LEFT JOIN organizations ON cases.org_id=organizations.id"
1305  + " LEFT JOIN data_sources ON "
1306  + tableName
1307  + ".data_source_id=data_sources.id"
1308  + " WHERE value IN (";
1309  StringBuilder inValuesBuilder = new StringBuilder(sql);
1310  for (String value : values) {
1311  if (value != null) {
1312  inValuesBuilder.append("'");
1313  inValuesBuilder.append(CorrelationAttributeNormalizer.normalize(aType, value));
1314  inValuesBuilder.append("',");
1315  }
1316  }
1317  inValuesBuilder.deleteCharAt(inValuesBuilder.length() - 1); //delete last comma
1318  inValuesBuilder.append(")");
1319  return inValuesBuilder.toString();
1320  }
1321 
1336  private List<CorrelationAttributeInstance> getArtifactInstances(String sql, CorrelationAttributeInstance.Type aType) throws CorrelationAttributeNormalizationException, CentralRepoException {
1337  Connection conn = connect();
1338  List<CorrelationAttributeInstance> artifactInstances = new ArrayList<>();
1339  CorrelationAttributeInstance artifactInstance;
1340  PreparedStatement preparedStatement = null;
1341  ResultSet resultSet = null;
1342  try {
1343  preparedStatement = conn.prepareStatement(sql);
1344  resultSet = preparedStatement.executeQuery();
1345  while (resultSet.next()) {
1346  artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType);
1347  artifactInstances.add(artifactInstance);
1348  }
1349  } catch (SQLException ex) {
1350  throw new CentralRepoException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS
1351  } finally {
1352  CentralRepoDbUtil.closeStatement(preparedStatement);
1353  CentralRepoDbUtil.closeResultSet(resultSet);
1354  CentralRepoDbUtil.closeConnection(conn);
1355  }
1356  return artifactInstances;
1357  }
1358 
1369  @Override
1370  public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
1371  String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
1372 
1373  Connection conn = connect();
1374 
1375  Long instanceCount = 0L;
1376  PreparedStatement preparedStatement = null;
1377  ResultSet resultSet = null;
1378 
1379  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
1380  String sql
1381  = "SELECT count(*) FROM "
1382  + tableName
1383  + " WHERE value=?";
1384 
1385  try {
1386  preparedStatement = conn.prepareStatement(sql);
1387  preparedStatement.setString(1, normalizedValue);
1388  resultSet = preparedStatement.executeQuery();
1389  resultSet.next();
1390  instanceCount = resultSet.getLong(1);
1391  } catch (SQLException ex) {
1392  throw new CentralRepoException("Error getting count of artifact instances by artifactType and artifactValue.", ex); // NON-NLS
1393  } finally {
1394  CentralRepoDbUtil.closeStatement(preparedStatement);
1395  CentralRepoDbUtil.closeResultSet(resultSet);
1396  CentralRepoDbUtil.closeConnection(conn);
1397  }
1398 
1399  return instanceCount;
1400  }
1401 
1402  @Override
1403  public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws CentralRepoException, CorrelationAttributeNormalizationException {
1404  if (corAttr == null) {
1405  throw new CentralRepoException("CorrelationAttribute is null");
1406  }
1407  Double uniqueTypeValueTuples = getCountUniqueCaseDataSourceTuplesHavingTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()).doubleValue();
1408  Double uniqueCaseDataSourceTuples = getCountUniqueDataSources().doubleValue();
1409  Double commonalityPercentage = uniqueTypeValueTuples / uniqueCaseDataSourceTuples * 100;
1410  return commonalityPercentage.intValue();
1411  }
1412 
1423  @Override
1424  public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
1425  String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
1426 
1427  Connection conn = connect();
1428 
1429  Long instanceCount = 0L;
1430  PreparedStatement preparedStatement = null;
1431  ResultSet resultSet = null;
1432 
1433  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
1434  String sql
1435  = "SELECT count(*) FROM (SELECT DISTINCT case_id, data_source_id FROM "
1436  + tableName
1437  + " WHERE value=?) AS "
1438  + tableName
1439  + "_distinct_case_data_source_tuple";
1440 
1441  try {
1442  preparedStatement = conn.prepareStatement(sql);
1443  preparedStatement.setString(1, normalizedValue);
1444  resultSet = preparedStatement.executeQuery();
1445  resultSet.next();
1446  instanceCount = resultSet.getLong(1);
1447  } catch (SQLException ex) {
1448  throw new CentralRepoException("Error counting unique caseDisplayName/dataSource tuples having artifactType and artifactValue.", ex); // NON-NLS
1449  } finally {
1450  CentralRepoDbUtil.closeStatement(preparedStatement);
1451  CentralRepoDbUtil.closeResultSet(resultSet);
1452  CentralRepoDbUtil.closeConnection(conn);
1453  }
1454 
1455  return instanceCount;
1456  }
1457 
1458  @Override
1459  public Long getCountUniqueDataSources() throws CentralRepoException {
1460  Connection conn = connect();
1461 
1462  Long instanceCount = 0L;
1463  PreparedStatement preparedStatement = null;
1464  ResultSet resultSet = null;
1465 
1466  String stmt = "SELECT count(*) FROM data_sources";
1467 
1468  try {
1469  preparedStatement = conn.prepareStatement(stmt);
1470  resultSet = preparedStatement.executeQuery();
1471  resultSet.next();
1472  instanceCount = resultSet.getLong(1);
1473  } catch (SQLException ex) {
1474  throw new CentralRepoException("Error counting data sources.", ex); // NON-NLS
1475  } finally {
1476  CentralRepoDbUtil.closeStatement(preparedStatement);
1477  CentralRepoDbUtil.closeResultSet(resultSet);
1478  CentralRepoDbUtil.closeConnection(conn);
1479  }
1480 
1481  return instanceCount;
1482  }
1483 
1495  @Override
1496  public Long getCountArtifactInstancesByCaseDataSource(CorrelationDataSource correlationDataSource) throws CentralRepoException {
1497  Connection conn = connect();
1498 
1499  Long instanceCount = 0L;
1500  List<CorrelationAttributeInstance.Type> artifactTypes = getDefinedCorrelationTypes();
1501  PreparedStatement preparedStatement = null;
1502  ResultSet resultSet = null;
1503 
1504  //Create query to get count of all instances in the database for the specified case specific data source
1505  String sql = "SELECT 0 ";
1506 
1507  for (CorrelationAttributeInstance.Type type : artifactTypes) {
1508  String table_name = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
1509  sql
1510  += "+ (SELECT count(*) FROM "
1511  + table_name
1512  + " WHERE data_source_id=" + correlationDataSource.getID() + ")";
1513  }
1514  try {
1515  preparedStatement = conn.prepareStatement(sql);
1516 
1517  resultSet = preparedStatement.executeQuery();
1518  resultSet.next();
1519  instanceCount = resultSet.getLong(1);
1520  } catch (SQLException ex) {
1521  throw new CentralRepoException("Error counting artifact instances by caseName/dataSource.", ex); // NON-NLS
1522  } finally {
1523  CentralRepoDbUtil.closeStatement(preparedStatement);
1524  CentralRepoDbUtil.closeResultSet(resultSet);
1525  CentralRepoDbUtil.closeConnection(conn);
1526  }
1527 
1528  return instanceCount;
1529  }
1530 
1538  @Override
1539  public void addAttributeInstanceBulk(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
1540 
1541  if (eamArtifact.getCorrelationType() == null) {
1542  throw new CentralRepoException("Correlation type is null");
1543  }
1544 
1545  synchronized (bulkArtifacts) {
1546  if (bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())) == null) {
1547  bulkArtifacts.put(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()), new ArrayList<>());
1548  }
1549  bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())).add(eamArtifact);
1550  bulkArtifactsCount++;
1551 
1552  if (bulkArtifactsCount >= bulkArtifactsThreshold) {
1553  commitAttributeInstancesBulk();
1554  }
1555  }
1556  }
1557 
1563  protected abstract String getConflictClause();
1564 
1569  @Override
1570  public void commitAttributeInstancesBulk() throws CentralRepoException {
1571  List<CorrelationAttributeInstance.Type> artifactTypes = getDefinedCorrelationTypes();
1572 
1573  Connection conn = connect();
1574  PreparedStatement bulkPs = null;
1575 
1576  try {
1577  synchronized (bulkArtifacts) {
1578  if (bulkArtifactsCount == 0) {
1579  return;
1580  }
1581 
1582  for (String tableName : bulkArtifacts.keySet()) {
1583 
1584  String sql
1585  = "INSERT INTO "
1586  + tableName
1587  + " (case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
1588  + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "
1589  + "(SELECT id FROM data_sources WHERE datasource_obj_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
1590  + getConflictClause();
1591 
1592  bulkPs = conn.prepareStatement(sql);
1593 
1594  Collection<CorrelationAttributeInstance> eamArtifacts = bulkArtifacts.get(tableName);
1595  for (CorrelationAttributeInstance eamArtifact : eamArtifacts) {
1596 
1597  if (!eamArtifact.getCorrelationValue().isEmpty()) {
1598 
1599  if (eamArtifact.getCorrelationCase() == null) {
1600  throw new CentralRepoException("CorrelationAttributeInstance case is null for: "
1601  + "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
1602  + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
1603  + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
1604  }
1605  if (eamArtifact.getCorrelationDataSource() == null) {
1606  throw new CentralRepoException("CorrelationAttributeInstance data source is null for: "
1607  + "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
1608  + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
1609  + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
1610  }
1611  if (eamArtifact.getKnownStatus() == null) {
1612  throw new CentralRepoException("CorrelationAttributeInstance known status is null for: "
1613  + "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
1614  + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
1615  + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()
1616  + "\n\tEam Instance: "
1617  + "\n\t\tCaseId: " + eamArtifact.getCorrelationDataSource().getCaseID()
1618  + "\n\t\tDeviceID: " + eamArtifact.getCorrelationDataSource().getDeviceID());
1619  }
1620 
1621  if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) {
1622  bulkPs.setString(1, eamArtifact.getCorrelationCase().getCaseUUID());
1623  bulkPs.setLong(2, eamArtifact.getCorrelationDataSource().getDataSourceObjectID());
1624  bulkPs.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID());
1625  bulkPs.setString(4, eamArtifact.getCorrelationValue());
1626  bulkPs.setString(5, eamArtifact.getFilePath());
1627  bulkPs.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue());
1628  if ("".equals(eamArtifact.getComment())) {
1629  bulkPs.setNull(7, Types.INTEGER);
1630  } else {
1631  bulkPs.setString(7, eamArtifact.getComment());
1632  }
1633  bulkPs.setLong(8, eamArtifact.getFileObjectId());
1634  bulkPs.addBatch();
1635  } else {
1636  logger.log(Level.WARNING, ("Artifact value too long for central repository."
1637  + "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
1638  + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
1639  + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue())
1640  + "\n\tEam Instance: "
1641  + "\n\t\tCaseId: " + eamArtifact.getCorrelationDataSource().getCaseID()
1642  + "\n\t\tDeviceID: " + eamArtifact.getCorrelationDataSource().getDeviceID()
1643  + "\n\t\tFilePath: " + eamArtifact.getFilePath());
1644  }
1645  }
1646 
1647  }
1648 
1649  bulkPs.executeBatch();
1650  bulkArtifacts.get(tableName).clear();
1651  }
1652 
1653  TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
1654  HealthMonitor.submitTimingMetric(timingMetric);
1655 
1656  // Reset state
1657  bulkArtifactsCount = 0;
1658  }
1659  } catch (SQLException ex) {
1660  throw new CentralRepoException("Error inserting bulk artifacts.", ex); // NON-NLS
1661  } finally {
1662  CentralRepoDbUtil.closeStatement(bulkPs);
1663  CentralRepoDbUtil.closeConnection(conn);
1664  }
1665  }
1666 
1670  @Override
1671  public void bulkInsertCases(List<CorrelationCase> cases) throws CentralRepoException {
1672  if (cases == null) {
1673  throw new CentralRepoException("cases argument is null");
1674  }
1675 
1676  if (cases.isEmpty()) {
1677  return;
1678  }
1679 
1680  Connection conn = connect();
1681 
1682  int counter = 0;
1683  PreparedStatement bulkPs = null;
1684  try {
1685  String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, "
1686  + "examiner_name, examiner_email, examiner_phone, notes) "
1687  + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) "
1688  + getConflictClause();
1689  bulkPs = conn.prepareStatement(sql);
1690 
1691  for (CorrelationCase eamCase : cases) {
1692  bulkPs.setString(1, eamCase.getCaseUUID());
1693  if (null == eamCase.getOrg()) {
1694  bulkPs.setNull(2, Types.INTEGER);
1695  } else {
1696  bulkPs.setInt(2, eamCase.getOrg().getOrgID());
1697  }
1698  bulkPs.setString(3, eamCase.getDisplayName());
1699  bulkPs.setString(4, eamCase.getCreationDate());
1700 
1701  if ("".equals(eamCase.getCaseNumber())) {
1702  bulkPs.setNull(5, Types.INTEGER);
1703  } else {
1704  bulkPs.setString(5, eamCase.getCaseNumber());
1705  }
1706  if ("".equals(eamCase.getExaminerName())) {
1707  bulkPs.setNull(6, Types.INTEGER);
1708  } else {
1709  bulkPs.setString(6, eamCase.getExaminerName());
1710  }
1711  if ("".equals(eamCase.getExaminerEmail())) {
1712  bulkPs.setNull(7, Types.INTEGER);
1713  } else {
1714  bulkPs.setString(7, eamCase.getExaminerEmail());
1715  }
1716  if ("".equals(eamCase.getExaminerPhone())) {
1717  bulkPs.setNull(8, Types.INTEGER);
1718  } else {
1719  bulkPs.setString(8, eamCase.getExaminerPhone());
1720  }
1721  if ("".equals(eamCase.getNotes())) {
1722  bulkPs.setNull(9, Types.INTEGER);
1723  } else {
1724  bulkPs.setString(9, eamCase.getNotes());
1725  }
1726 
1727  bulkPs.addBatch();
1728 
1729  counter++;
1730 
1731  // limit a batch's max size to bulkArtifactsThreshold
1732  if (counter >= bulkArtifactsThreshold) {
1733  bulkPs.executeBatch();
1734  counter = 0;
1735  }
1736  }
1737  // send the remaining batch records
1738  bulkPs.executeBatch();
1739  } catch (SQLException ex) {
1740  throw new CentralRepoException("Error inserting bulk cases.", ex); // NON-NLS
1741  } finally {
1742  CentralRepoDbUtil.closeStatement(bulkPs);
1743  CentralRepoDbUtil.closeConnection(conn);
1744  }
1745  }
1746 
1756  @Override
1757  public void updateAttributeInstanceComment(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
1758 
1759  if (eamArtifact == null) {
1760  throw new CentralRepoException("CorrelationAttributeInstance is null");
1761  }
1762  if (eamArtifact.getCorrelationCase() == null) {
1763  throw new CentralRepoException("Correlation case is null");
1764  }
1765  if (eamArtifact.getCorrelationDataSource() == null) {
1766  throw new CentralRepoException("Correlation data source is null");
1767  }
1768  Connection conn = connect();
1769  PreparedStatement preparedQuery = null;
1770  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
1771  String sqlUpdate
1772  = "UPDATE "
1773  + tableName
1774  + " SET comment=? "
1775  + "WHERE case_id=? "
1776  + "AND data_source_id=? "
1777  + "AND value=? "
1778  + "AND file_path=?";
1779 
1780  try {
1781  preparedQuery = conn.prepareStatement(sqlUpdate);
1782  preparedQuery.setString(1, eamArtifact.getComment());
1783  preparedQuery.setInt(2, eamArtifact.getCorrelationCase().getID());
1784  preparedQuery.setInt(3, eamArtifact.getCorrelationDataSource().getID());
1785  preparedQuery.setString(4, eamArtifact.getCorrelationValue());
1786  preparedQuery.setString(5, eamArtifact.getFilePath().toLowerCase());
1787  preparedQuery.executeUpdate();
1788  } catch (SQLException ex) {
1789  throw new CentralRepoException("Error getting/setting artifact instance comment=" + eamArtifact.getComment(), ex); // NON-NLS
1790  } finally {
1791  CentralRepoDbUtil.closeStatement(preparedQuery);
1792  CentralRepoDbUtil.closeConnection(conn);
1793  }
1794  }
1795 
1810  @Override
1811  public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
1812  CorrelationDataSource correlationDataSource, long objectID) throws CentralRepoException, CorrelationAttributeNormalizationException {
1813 
1814  if (correlationCase == null) {
1815  throw new CentralRepoException("Correlation case is null");
1816  }
1817 
1818  Connection conn = connect();
1819 
1820  PreparedStatement preparedStatement = null;
1821  ResultSet resultSet = null;
1822  CorrelationAttributeInstance correlationAttributeInstance = null;
1823 
1824  try {
1825 
1826  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
1827  String sql
1828  = "SELECT id, value, file_path, known_status, comment FROM "
1829  + tableName
1830  + " WHERE case_id=?"
1831  + " AND file_obj_id=?";
1832 
1833  preparedStatement = conn.prepareStatement(sql);
1834  preparedStatement.setInt(1, correlationCase.getID());
1835  preparedStatement.setInt(2, (int) objectID);
1836  resultSet = preparedStatement.executeQuery();
1837  if (resultSet.next()) {
1838  int instanceId = resultSet.getInt(1);
1839  String value = resultSet.getString(2);
1840  String filePath = resultSet.getString(3);
1841  int knownStatus = resultSet.getInt(4);
1842  String comment = resultSet.getString(5);
1843 
1844  correlationAttributeInstance = new CorrelationAttributeInstance(type, value,
1845  instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus), objectID);
1846  }
1847  } catch (SQLException ex) {
1848  throw new CentralRepoException("Error getting notable artifact instances.", ex); // NON-NLS
1849  } finally {
1850  CentralRepoDbUtil.closeStatement(preparedStatement);
1851  CentralRepoDbUtil.closeResultSet(resultSet);
1852  CentralRepoDbUtil.closeConnection(conn);
1853  }
1854 
1855  return correlationAttributeInstance;
1856  }
1857 
1872  @Override
1873  public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
1874  CorrelationDataSource correlationDataSource, String value, String filePath) throws CentralRepoException, CorrelationAttributeNormalizationException {
1875 
1876  if (correlationCase == null) {
1877  throw new CentralRepoException("Correlation case is null");
1878  }
1879  if (correlationDataSource == null) {
1880  throw new CentralRepoException("Correlation data source is null");
1881  }
1882  if (filePath == null) {
1883  throw new CentralRepoException("Correlation file path is null");
1884  }
1885 
1886  Connection conn = connect();
1887 
1888  PreparedStatement preparedStatement = null;
1889  ResultSet resultSet = null;
1890  CorrelationAttributeInstance correlationAttributeInstance = null;
1891 
1892  try {
1893  String normalizedValue = CorrelationAttributeNormalizer.normalize(type, value);
1894 
1895  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
1896  String sql
1897  = "SELECT id, known_status, comment FROM "
1898  + tableName
1899  + " WHERE case_id=?"
1900  + " AND data_source_id=?"
1901  + " AND value=?"
1902  + " AND file_path=?";
1903 
1904  preparedStatement = conn.prepareStatement(sql);
1905  preparedStatement.setInt(1, correlationCase.getID());
1906  preparedStatement.setInt(2, correlationDataSource.getID());
1907  preparedStatement.setString(3, normalizedValue);
1908  preparedStatement.setString(4, filePath.toLowerCase());
1909  resultSet = preparedStatement.executeQuery();
1910  if (resultSet.next()) {
1911  int instanceId = resultSet.getInt(1);
1912  int knownStatus = resultSet.getInt(2);
1913  String comment = resultSet.getString(3);
1914  //null objectId used because we only fall back to using this method when objectID was not available
1915  correlationAttributeInstance = new CorrelationAttributeInstance(type, value,
1916  instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus), null);
1917  }
1918  } catch (SQLException ex) {
1919  throw new CentralRepoException("Error getting notable artifact instances.", ex); // NON-NLS
1920  } finally {
1921  CentralRepoDbUtil.closeStatement(preparedStatement);
1922  CentralRepoDbUtil.closeResultSet(resultSet);
1923  CentralRepoDbUtil.closeConnection(conn);
1924  }
1925 
1926  return correlationAttributeInstance;
1927  }
1928 
1939  @Override
1940  public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws CentralRepoException {
1941  if (eamArtifact == null) {
1942  throw new CentralRepoException("CorrelationAttribute is null");
1943  }
1944  if (knownStatus == null) {
1945  throw new CentralRepoException("Known status is null");
1946  }
1947 
1948  if (eamArtifact.getCorrelationCase() == null) {
1949  throw new CentralRepoException("Correlation case is null");
1950  }
1951  if (eamArtifact.getCorrelationDataSource() == null) {
1952  throw new CentralRepoException("Correlation data source is null");
1953  }
1954 
1955  Connection conn = connect();
1956 
1957  PreparedStatement preparedUpdate = null;
1958  PreparedStatement preparedQuery = null;
1959  ResultSet resultSet = null;
1960 
1961  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
1962 
1963  String sqlQuery
1964  = "SELECT id FROM "
1965  + tableName
1966  + " WHERE case_id=? "
1967  + "AND data_source_id=? "
1968  + "AND value=? "
1969  + "AND file_path=?";
1970 
1971  String sqlUpdate
1972  = "UPDATE "
1973  + tableName
1974  + " SET known_status=?, comment=? "
1975  + "WHERE id=?";
1976 
1977  try {
1978  preparedQuery = conn.prepareStatement(sqlQuery);
1979  preparedQuery.setInt(1, eamArtifact.getCorrelationCase().getID());
1980  preparedQuery.setInt(2, eamArtifact.getCorrelationDataSource().getID());
1981  preparedQuery.setString(3, eamArtifact.getCorrelationValue());
1982  preparedQuery.setString(4, eamArtifact.getFilePath());
1983  resultSet = preparedQuery.executeQuery();
1984  if (resultSet.next()) {
1985  int instance_id = resultSet.getInt("id");
1986  preparedUpdate = conn.prepareStatement(sqlUpdate);
1987 
1988  preparedUpdate.setByte(1, knownStatus.getFileKnownValue());
1989  // NOTE: if the user tags the same instance as BAD multiple times,
1990  // the comment from the most recent tagging is the one that will
1991  // prevail in the DB.
1992  if ("".equals(eamArtifact.getComment())) {
1993  preparedUpdate.setNull(2, Types.INTEGER);
1994  } else {
1995  preparedUpdate.setString(2, eamArtifact.getComment());
1996  }
1997  preparedUpdate.setInt(3, instance_id);
1998 
1999  preparedUpdate.executeUpdate();
2000  } else {
2001  // In this case, the user is tagging something that isn't in the database,
2002  // which means the case and/or datasource may also not be in the database.
2003  // We could improve effiency by keeping a list of all datasources and cases
2004  // in the database, but we don't expect the user to be tagging large numbers
2005  // of items (that didn't have the CE ingest module run on them) at once.
2006  CorrelationCase correlationCaseWithId = getCaseByUUID(eamArtifact.getCorrelationCase().getCaseUUID());
2007  if (null == getDataSource(correlationCaseWithId, eamArtifact.getCorrelationDataSource().getDataSourceObjectID())) {
2008  newDataSource(eamArtifact.getCorrelationDataSource());
2009  }
2010  eamArtifact.setKnownStatus(knownStatus);
2011  addArtifactInstance(eamArtifact);
2012  }
2013 
2014  } catch (SQLException ex) {
2015  throw new CentralRepoException("Error getting/setting artifact instance knownStatus=" + knownStatus.getName(), ex); // NON-NLS
2016  } finally {
2017  CentralRepoDbUtil.closeStatement(preparedUpdate);
2018  CentralRepoDbUtil.closeStatement(preparedQuery);
2019  CentralRepoDbUtil.closeResultSet(resultSet);
2020  CentralRepoDbUtil.closeConnection(conn);
2021  }
2022  }
2023 
2032  @Override
2033  public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
2034 
2035  String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
2036 
2037  Connection conn = connect();
2038 
2039  Long badInstances = 0L;
2040  PreparedStatement preparedStatement = null;
2041  ResultSet resultSet = null;
2042 
2043  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
2044  String sql
2045  = "SELECT count(*) FROM "
2046  + tableName
2047  + " WHERE value=? AND known_status=?";
2048 
2049  try {
2050  preparedStatement = conn.prepareStatement(sql);
2051  preparedStatement.setString(1, normalizedValue);
2052  preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue());
2053  resultSet = preparedStatement.executeQuery();
2054  resultSet.next();
2055  badInstances = resultSet.getLong(1);
2056  } catch (SQLException ex) {
2057  throw new CentralRepoException("Error getting count of notable artifact instances.", ex); // NON-NLS
2058  } finally {
2059  CentralRepoDbUtil.closeStatement(preparedStatement);
2060  CentralRepoDbUtil.closeResultSet(resultSet);
2061  CentralRepoDbUtil.closeConnection(conn);
2062  }
2063 
2064  return badInstances;
2065  }
2066 
2079  @Override
2080  public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
2081 
2082  String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
2083 
2084  Connection conn = connect();
2085 
2086  Collection<String> caseNames = new LinkedHashSet<>();
2087 
2088  PreparedStatement preparedStatement = null;
2089  ResultSet resultSet = null;
2090 
2091  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
2092  String sql
2093  = "SELECT DISTINCT case_name FROM "
2094  + tableName
2095  + " INNER JOIN cases ON "
2096  + tableName
2097  + ".case_id=cases.id WHERE "
2098  + tableName
2099  + ".value=? AND "
2100  + tableName
2101  + ".known_status=?";
2102 
2103  try {
2104  preparedStatement = conn.prepareStatement(sql);
2105  preparedStatement.setString(1, normalizedValue);
2106  preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue());
2107  resultSet = preparedStatement.executeQuery();
2108  while (resultSet.next()) {
2109  caseNames.add(resultSet.getString("case_name"));
2110  }
2111  } catch (SQLException ex) {
2112  throw new CentralRepoException("Error getting notable artifact instances.", ex); // NON-NLS
2113  } finally {
2114  CentralRepoDbUtil.closeStatement(preparedStatement);
2115  CentralRepoDbUtil.closeResultSet(resultSet);
2116  CentralRepoDbUtil.closeConnection(conn);
2117  }
2118 
2119  return caseNames.stream().collect(Collectors.toList());
2120  }
2121 
2134  @Override
2135  public List<String> getListCasesHavingArtifactInstances(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
2136 
2137  String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
2138 
2139  Connection conn = connect();
2140 
2141  Collection<String> caseNames = new LinkedHashSet<>();
2142 
2143  PreparedStatement preparedStatement = null;
2144  ResultSet resultSet = null;
2145 
2146  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(aType);
2147  String sql
2148  = "SELECT DISTINCT case_name FROM "
2149  + tableName
2150  + " INNER JOIN cases ON "
2151  + tableName
2152  + ".case_id=cases.id WHERE "
2153  + tableName
2154  + ".value=? ";
2155 
2156  try {
2157  preparedStatement = conn.prepareStatement(sql);
2158  preparedStatement.setString(1, normalizedValue);
2159  resultSet = preparedStatement.executeQuery();
2160  while (resultSet.next()) {
2161  caseNames.add(resultSet.getString("case_name"));
2162  }
2163  } catch (SQLException ex) {
2164  throw new CentralRepoException("Error getting notable artifact instances.", ex); // NON-NLS
2165  } finally {
2166  CentralRepoDbUtil.closeStatement(preparedStatement);
2167  CentralRepoDbUtil.closeResultSet(resultSet);
2168  CentralRepoDbUtil.closeConnection(conn);
2169  }
2170 
2171  return caseNames.stream().collect(Collectors.toList());
2172  }
2173 
2181  @Override
2182  public void deleteReferenceSet(int referenceSetID) throws CentralRepoException {
2183  deleteReferenceSetEntries(referenceSetID);
2184  deleteReferenceSetEntry(referenceSetID);
2185  }
2186 
2194  private void deleteReferenceSetEntry(int referenceSetID) throws CentralRepoException {
2195  Connection conn = connect();
2196 
2197  PreparedStatement preparedStatement = null;
2198  String sql = "DELETE FROM reference_sets WHERE id=?";
2199 
2200  try {
2201  preparedStatement = conn.prepareStatement(sql);
2202  preparedStatement.setInt(1, referenceSetID);
2203  preparedStatement.executeUpdate();
2204  } catch (SQLException ex) {
2205  throw new CentralRepoException("Error deleting reference set " + referenceSetID, ex); // NON-NLS
2206  } finally {
2207  CentralRepoDbUtil.closeStatement(preparedStatement);
2208  CentralRepoDbUtil.closeConnection(conn);
2209  }
2210  }
2211 
2220  private void deleteReferenceSetEntries(int referenceSetID) throws CentralRepoException {
2221  Connection conn = connect();
2222 
2223  PreparedStatement preparedStatement = null;
2224  String sql = "DELETE FROM %s WHERE reference_set_id=?";
2225 
2226  // When other reference types are added, this will need to loop over all the tables
2227  String fileTableName = CentralRepoDbUtil.correlationTypeToReferenceTableName(getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
2228 
2229  try {
2230  preparedStatement = conn.prepareStatement(String.format(sql, fileTableName));
2231  preparedStatement.setInt(1, referenceSetID);
2232  preparedStatement.executeUpdate();
2233  } catch (SQLException ex) {
2234  throw new CentralRepoException("Error deleting files from reference set " + referenceSetID, ex); // NON-NLS
2235  } finally {
2236  CentralRepoDbUtil.closeStatement(preparedStatement);
2237  CentralRepoDbUtil.closeConnection(conn);
2238  }
2239  }
2240 
2254  @Override
2255  public boolean referenceSetIsValid(int referenceSetID, String setName, String version) throws CentralRepoException {
2256  CentralRepoFileSet refSet = this.getReferenceSetByID(referenceSetID);
2257  if (refSet == null) {
2258  return false;
2259  }
2260 
2261  return (refSet.getSetName().equals(setName) && refSet.getVersion().equals(version));
2262  }
2263 
2275  @Override
2276  public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException {
2277  return isValueInReferenceSet(hash, referenceSetID, CorrelationAttributeInstance.FILES_TYPE_ID);
2278  }
2279 
2289  @Override
2290  public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws CentralRepoException, CorrelationAttributeNormalizationException {
2291 
2292  String normalizeValued = CorrelationAttributeNormalizer.normalize(this.getCorrelationTypeById(correlationTypeID), value);
2293 
2294  Connection conn = connect();
2295 
2296  Long matchingInstances = 0L;
2297  PreparedStatement preparedStatement = null;
2298  ResultSet resultSet = null;
2299  String sql = "SELECT count(*) FROM %s WHERE value=? AND reference_set_id=?";
2300 
2301  String fileTableName = CentralRepoDbUtil.correlationTypeToReferenceTableName(getCorrelationTypeById(correlationTypeID));
2302 
2303  try {
2304  preparedStatement = conn.prepareStatement(String.format(sql, fileTableName));
2305  preparedStatement.setString(1, normalizeValued);
2306  preparedStatement.setInt(2, referenceSetID);
2307  resultSet = preparedStatement.executeQuery();
2308  resultSet.next();
2309  matchingInstances = resultSet.getLong(1);
2310  } catch (SQLException ex) {
2311  throw new CentralRepoException("Error determining if value (" + normalizeValued + ") is in reference set " + referenceSetID, ex); // NON-NLS
2312  } finally {
2313  CentralRepoDbUtil.closeStatement(preparedStatement);
2314  CentralRepoDbUtil.closeResultSet(resultSet);
2315  CentralRepoDbUtil.closeConnection(conn);
2316  }
2317 
2318  return 0 < matchingInstances;
2319  }
2320 
2329  @Override
2330  public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException {
2331 
2332  //this should be done here so that we can be certain that aType and value are valid before we proceed
2333  String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, value);
2334 
2335  // TEMP: Only support file correlation type
2336  if (aType.getId() != CorrelationAttributeInstance.FILES_TYPE_ID) {
2337  return false;
2338  }
2339 
2340  Connection conn = connect();
2341 
2342  Long badInstances = 0L;
2343  PreparedStatement preparedStatement = null;
2344  ResultSet resultSet = null;
2345  String sql = "SELECT count(*) FROM %s WHERE value=? AND known_status=?";
2346 
2347  try {
2348  preparedStatement = conn.prepareStatement(String.format(sql, CentralRepoDbUtil.correlationTypeToReferenceTableName(aType)));
2349  preparedStatement.setString(1, normalizeValued);
2350  preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue());
2351  resultSet = preparedStatement.executeQuery();
2352  resultSet.next();
2353  badInstances = resultSet.getLong(1);
2354  } catch (SQLException ex) {
2355  throw new CentralRepoException("Error determining if artifact is notable by reference.", ex); // NON-NLS
2356  } finally {
2357  CentralRepoDbUtil.closeStatement(preparedStatement);
2358  CentralRepoDbUtil.closeResultSet(resultSet);
2359  CentralRepoDbUtil.closeConnection(conn);
2360  }
2361 
2362  return 0 < badInstances;
2363  }
2364 
2373  @Override
2374  public void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws CentralRepoException {
2375  if (type == null) {
2376  throw new CentralRepoException("Correlation type is null");
2377  }
2378 
2379  if (instanceTableCallback == null) {
2380  throw new CentralRepoException("Callback interface is null");
2381  }
2382 
2383  Connection conn = connect();
2384  PreparedStatement preparedStatement = null;
2385  ResultSet resultSet = null;
2386  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
2387  StringBuilder sql = new StringBuilder();
2388  sql.append("select * from ");
2389  sql.append(tableName);
2390 
2391  try {
2392  preparedStatement = conn.prepareStatement(sql.toString());
2393  resultSet = preparedStatement.executeQuery();
2394  instanceTableCallback.process(resultSet);
2395  } catch (SQLException ex) {
2396  throw new CentralRepoException("Error getting all artifact instances from instances table", ex);
2397  } finally {
2398  CentralRepoDbUtil.closeStatement(preparedStatement);
2399  CentralRepoDbUtil.closeResultSet(resultSet);
2400  CentralRepoDbUtil.closeConnection(conn);
2401  }
2402  }
2403 
2413  @Override
2414  public void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException {
2415  if (type == null) {
2416  throw new CentralRepoException("Correlation type is null");
2417  }
2418 
2419  if (instanceTableCallback == null) {
2420  throw new CentralRepoException("Callback interface is null");
2421  }
2422 
2423  if (whereClause == null) {
2424  throw new CentralRepoException("Where clause is null");
2425  }
2426 
2427  Connection conn = connect();
2428  PreparedStatement preparedStatement = null;
2429  ResultSet resultSet = null;
2430  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
2431  StringBuilder sql = new StringBuilder(300);
2432  sql.append("select * from ")
2433  .append(tableName)
2434  .append(" WHERE ")
2435  .append(whereClause);
2436 
2437  try {
2438  preparedStatement = conn.prepareStatement(sql.toString());
2439  resultSet = preparedStatement.executeQuery();
2440  instanceTableCallback.process(resultSet);
2441  } catch (SQLException ex) {
2442  throw new CentralRepoException("Error getting all artifact instances from instances table", ex);
2443  } finally {
2444  CentralRepoDbUtil.closeStatement(preparedStatement);
2445  CentralRepoDbUtil.closeResultSet(resultSet);
2446  CentralRepoDbUtil.closeConnection(conn);
2447  }
2448  }
2449 
2458  @Override
2459  public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException {
2460 
2461  if (instanceTableCallback == null) {
2462  throw new CentralRepoException("Callback interface is null");
2463  }
2464 
2465  if (selectClause == null) {
2466  throw new CentralRepoException("Select clause is null");
2467  }
2468 
2469  Connection conn = connect();
2470  PreparedStatement preparedStatement = null;
2471  ResultSet resultSet = null;
2472  StringBuilder sql = new StringBuilder(300);
2473  sql.append("select ")
2474  .append(selectClause);
2475 
2476  try {
2477  preparedStatement = conn.prepareStatement(sql.toString());
2478  resultSet = preparedStatement.executeQuery();
2479  instanceTableCallback.process(resultSet);
2480  } catch (SQLException ex) {
2481  throw new CentralRepoException("Error running query", ex);
2482  } finally {
2483  CentralRepoDbUtil.closeStatement(preparedStatement);
2484  CentralRepoDbUtil.closeResultSet(resultSet);
2485  CentralRepoDbUtil.closeConnection(conn);
2486  }
2487  }
2488 
2489  @Override
2490  public CentralRepoOrganization newOrganization(CentralRepoOrganization eamOrg) throws CentralRepoException {
2491  if (eamOrg == null) {
2492  throw new CentralRepoException("EamOrganization is null");
2493  } else if (eamOrg.getOrgID() != -1) {
2494  throw new CentralRepoException("EamOrganization already has an ID");
2495  }
2496 
2497  Connection conn = connect();
2498  ResultSet generatedKeys = null;
2499  PreparedStatement preparedStatement = null;
2500  String sql = "INSERT INTO organizations(org_name, poc_name, poc_email, poc_phone) VALUES (?, ?, ?, ?) "
2501  + getConflictClause();
2502 
2503  try {
2504  preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
2505  preparedStatement.setString(1, eamOrg.getName());
2506  preparedStatement.setString(2, eamOrg.getPocName());
2507  preparedStatement.setString(3, eamOrg.getPocEmail());
2508  preparedStatement.setString(4, eamOrg.getPocPhone());
2509 
2510  preparedStatement.executeUpdate();
2511  generatedKeys = preparedStatement.getGeneratedKeys();
2512  if (generatedKeys.next()) {
2513  eamOrg.setOrgID((int) generatedKeys.getLong(1));
2514  return eamOrg;
2515  } else {
2516  throw new SQLException("Creating user failed, no ID obtained.");
2517  }
2518  } catch (SQLException ex) {
2519  throw new CentralRepoException("Error inserting new organization.", ex); // NON-NLS
2520  } finally {
2521  CentralRepoDbUtil.closeStatement(preparedStatement);
2522  CentralRepoDbUtil.closeResultSet(generatedKeys);
2523  CentralRepoDbUtil.closeConnection(conn);
2524  }
2525  }
2526 
2534  @Override
2535  public List<CentralRepoOrganization> getOrganizations() throws CentralRepoException {
2536  Connection conn = connect();
2537 
2538  List<CentralRepoOrganization> orgs = new ArrayList<>();
2539  PreparedStatement preparedStatement = null;
2540  ResultSet resultSet = null;
2541  String sql = "SELECT * FROM organizations";
2542 
2543  try {
2544  preparedStatement = conn.prepareStatement(sql);
2545  resultSet = preparedStatement.executeQuery();
2546  while (resultSet.next()) {
2547  orgs.add(getEamOrganizationFromResultSet(resultSet));
2548  }
2549  return orgs;
2550 
2551  } catch (SQLException ex) {
2552  throw new CentralRepoException("Error getting all organizations.", ex); // NON-NLS
2553  } finally {
2554  CentralRepoDbUtil.closeStatement(preparedStatement);
2555  CentralRepoDbUtil.closeResultSet(resultSet);
2556  CentralRepoDbUtil.closeConnection(conn);
2557  }
2558  }
2559 
2569  @Override
2570  public CentralRepoOrganization getOrganizationByID(int orgID) throws CentralRepoException {
2571  Connection conn = connect();
2572 
2573  PreparedStatement preparedStatement = null;
2574  ResultSet resultSet = null;
2575  String sql = "SELECT * FROM organizations WHERE id=?";
2576 
2577  try {
2578  preparedStatement = conn.prepareStatement(sql);
2579  preparedStatement.setInt(1, orgID);
2580  resultSet = preparedStatement.executeQuery();
2581  resultSet.next();
2582  return getEamOrganizationFromResultSet(resultSet);
2583 
2584  } catch (SQLException ex) {
2585  throw new CentralRepoException("Error getting organization by id.", ex); // NON-NLS
2586  } finally {
2587  CentralRepoDbUtil.closeStatement(preparedStatement);
2588  CentralRepoDbUtil.closeResultSet(resultSet);
2589  CentralRepoDbUtil.closeConnection(conn);
2590  }
2591  }
2592 
2602  @Override
2603  public CentralRepoOrganization getReferenceSetOrganization(int referenceSetID) throws CentralRepoException {
2604 
2605  CentralRepoFileSet globalSet = getReferenceSetByID(referenceSetID);
2606  if (globalSet == null) {
2607  throw new CentralRepoException("Reference set with ID " + referenceSetID + " not found");
2608  }
2609  return (getOrganizationByID(globalSet.getOrgID()));
2610  }
2611 
2619  private void testArgument(CentralRepoOrganization org) throws CentralRepoException {
2620  if (org == null) {
2621  throw new CentralRepoException("EamOrganization is null");
2622  } else if (org.getOrgID() == -1) {
2623  throw new CentralRepoException("Organization has -1 row ID");
2624  }
2625  }
2626 
2635  @Override
2636  public void updateOrganization(CentralRepoOrganization updatedOrganization) throws CentralRepoException {
2637  testArgument(updatedOrganization);
2638 
2639  Connection conn = connect();
2640  PreparedStatement preparedStatement = null;
2641  String sql = "UPDATE organizations SET org_name = ?, poc_name = ?, poc_email = ?, poc_phone = ? WHERE id = ?";
2642  try {
2643  preparedStatement = conn.prepareStatement(sql);
2644  preparedStatement.setString(1, updatedOrganization.getName());
2645  preparedStatement.setString(2, updatedOrganization.getPocName());
2646  preparedStatement.setString(3, updatedOrganization.getPocEmail());
2647  preparedStatement.setString(4, updatedOrganization.getPocPhone());
2648  preparedStatement.setInt(5, updatedOrganization.getOrgID());
2649  preparedStatement.executeUpdate();
2650  } catch (SQLException ex) {
2651  throw new CentralRepoException("Error updating organization.", ex); // NON-NLS
2652  } finally {
2653  CentralRepoDbUtil.closeStatement(preparedStatement);
2654  CentralRepoDbUtil.closeConnection(conn);
2655  }
2656  }
2657 
2658  @Override
2659  public void deleteOrganization(CentralRepoOrganization organizationToDelete) throws CentralRepoException {
2660  testArgument(organizationToDelete);
2661 
2662  Connection conn = connect();
2663  PreparedStatement checkIfUsedStatement = null;
2664  ResultSet resultSet = null;
2665  String checkIfUsedSql = "SELECT (select count(*) FROM cases WHERE org_id=?) + (select count(*) FROM reference_sets WHERE org_id=?)";
2666  PreparedStatement deleteOrgStatement = null;
2667  String deleteOrgSql = "DELETE FROM organizations WHERE id=?";
2668  try {
2669  checkIfUsedStatement = conn.prepareStatement(checkIfUsedSql);
2670  checkIfUsedStatement.setInt(1, organizationToDelete.getOrgID());
2671  checkIfUsedStatement.setInt(2, organizationToDelete.getOrgID());
2672  resultSet = checkIfUsedStatement.executeQuery();
2673  resultSet.next();
2674  if (resultSet.getLong(1) > 0) {
2675  throw new CentralRepoException("Can not delete organization which is currently in use by a case or reference set in the central repository.");
2676  }
2677  deleteOrgStatement = conn.prepareStatement(deleteOrgSql);
2678  deleteOrgStatement.setInt(1, organizationToDelete.getOrgID());
2679  deleteOrgStatement.executeUpdate();
2680  } catch (SQLException ex) {
2681  throw new CentralRepoException("Error executing query when attempting to delete organization by id.", ex); // NON-NLS
2682  } finally {
2683  CentralRepoDbUtil.closeStatement(checkIfUsedStatement);
2684  CentralRepoDbUtil.closeStatement(deleteOrgStatement);
2685  CentralRepoDbUtil.closeResultSet(resultSet);
2686  CentralRepoDbUtil.closeConnection(conn);
2687  }
2688  }
2689 
2699  @Override
2700  public int newReferenceSet(CentralRepoFileSet eamGlobalSet) throws CentralRepoException {
2701  if (eamGlobalSet == null) {
2702  throw new CentralRepoException("EamGlobalSet is null");
2703  }
2704 
2705  if (eamGlobalSet.getFileKnownStatus() == null) {
2706  throw new CentralRepoException("File known status on the EamGlobalSet is null");
2707  }
2708 
2709  if (eamGlobalSet.getType() == null) {
2710  throw new CentralRepoException("Type on the EamGlobalSet is null");
2711  }
2712 
2713  Connection conn = connect();
2714 
2715  PreparedStatement preparedStatement1 = null;
2716  PreparedStatement preparedStatement2 = null;
2717  ResultSet resultSet = null;
2718  String sql1 = "INSERT INTO reference_sets(org_id, set_name, version, known_status, read_only, type, import_date) VALUES (?, ?, ?, ?, ?, ?, ?) "
2719  + getConflictClause();
2720  String sql2 = "SELECT id FROM reference_sets WHERE org_id=? AND set_name=? AND version=? AND import_date=? LIMIT 1";
2721 
2722  try {
2723  preparedStatement1 = conn.prepareStatement(sql1);
2724  preparedStatement1.setInt(1, eamGlobalSet.getOrgID());
2725  preparedStatement1.setString(2, eamGlobalSet.getSetName());
2726  preparedStatement1.setString(3, eamGlobalSet.getVersion());
2727  preparedStatement1.setInt(4, eamGlobalSet.getFileKnownStatus().getFileKnownValue());
2728  preparedStatement1.setBoolean(5, eamGlobalSet.isReadOnly());
2729  preparedStatement1.setInt(6, eamGlobalSet.getType().getId());
2730  preparedStatement1.setString(7, eamGlobalSet.getImportDate().toString());
2731 
2732  preparedStatement1.executeUpdate();
2733 
2734  preparedStatement2 = conn.prepareStatement(sql2);
2735  preparedStatement2.setInt(1, eamGlobalSet.getOrgID());
2736  preparedStatement2.setString(2, eamGlobalSet.getSetName());
2737  preparedStatement2.setString(3, eamGlobalSet.getVersion());
2738  preparedStatement2.setString(4, eamGlobalSet.getImportDate().toString());
2739 
2740  resultSet = preparedStatement2.executeQuery();
2741  resultSet.next();
2742  return resultSet.getInt("id");
2743 
2744  } catch (SQLException ex) {
2745  throw new CentralRepoException("Error inserting new global set.", ex); // NON-NLS
2746  } finally {
2747  CentralRepoDbUtil.closeStatement(preparedStatement1);
2748  CentralRepoDbUtil.closeStatement(preparedStatement2);
2749  CentralRepoDbUtil.closeResultSet(resultSet);
2750  CentralRepoDbUtil.closeConnection(conn);
2751  }
2752  }
2753 
2763  @Override
2764  public CentralRepoFileSet getReferenceSetByID(int referenceSetID) throws CentralRepoException {
2765  Connection conn = connect();
2766 
2767  PreparedStatement preparedStatement1 = null;
2768  ResultSet resultSet = null;
2769  String sql1 = "SELECT * FROM reference_sets WHERE id=?";
2770 
2771  try {
2772  preparedStatement1 = conn.prepareStatement(sql1);
2773  preparedStatement1.setInt(1, referenceSetID);
2774  resultSet = preparedStatement1.executeQuery();
2775  if (resultSet.next()) {
2776  return getEamGlobalSetFromResultSet(resultSet);
2777  } else {
2778  return null;
2779  }
2780 
2781  } catch (SQLException ex) {
2782  throw new CentralRepoException("Error getting reference set by id.", ex); // NON-NLS
2783  } finally {
2784  CentralRepoDbUtil.closeStatement(preparedStatement1);
2785  CentralRepoDbUtil.closeResultSet(resultSet);
2786  CentralRepoDbUtil.closeConnection(conn);
2787  }
2788  }
2789 
2799  @Override
2800  public List<CentralRepoFileSet> getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws CentralRepoException {
2801 
2802  if (correlationType == null) {
2803  throw new CentralRepoException("Correlation type is null");
2804  }
2805 
2806  List<CentralRepoFileSet> results = new ArrayList<>();
2807  Connection conn = connect();
2808 
2809  PreparedStatement preparedStatement1 = null;
2810  ResultSet resultSet = null;
2811  String sql1 = "SELECT * FROM reference_sets WHERE type=" + correlationType.getId();
2812 
2813  try {
2814  preparedStatement1 = conn.prepareStatement(sql1);
2815  resultSet = preparedStatement1.executeQuery();
2816  while (resultSet.next()) {
2817  results.add(getEamGlobalSetFromResultSet(resultSet));
2818  }
2819 
2820  } catch (SQLException ex) {
2821  throw new CentralRepoException("Error getting reference sets.", ex); // NON-NLS
2822  } finally {
2823  CentralRepoDbUtil.closeStatement(preparedStatement1);
2824  CentralRepoDbUtil.closeResultSet(resultSet);
2825  CentralRepoDbUtil.closeConnection(conn);
2826  }
2827  return results;
2828  }
2829 
2839  @Override
2840  public void addReferenceInstance(CentralRepoFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws CentralRepoException {
2841  if (eamGlobalFileInstance.getKnownStatus() == null) {
2842  throw new CentralRepoException("Known status of EamGlobalFileInstance is null");
2843  }
2844  if (correlationType == null) {
2845  throw new CentralRepoException("Correlation type is null");
2846  }
2847 
2848  Connection conn = connect();
2849 
2850  PreparedStatement preparedStatement = null;
2851 
2852  String sql = "INSERT INTO %s(reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) "
2853  + getConflictClause();
2854 
2855  try {
2856  preparedStatement = conn.prepareStatement(String.format(sql, CentralRepoDbUtil.correlationTypeToReferenceTableName(correlationType)));
2857  preparedStatement.setInt(1, eamGlobalFileInstance.getGlobalSetID());
2858  preparedStatement.setString(2, eamGlobalFileInstance.getMD5Hash());
2859  preparedStatement.setByte(3, eamGlobalFileInstance.getKnownStatus().getFileKnownValue());
2860  preparedStatement.setString(4, eamGlobalFileInstance.getComment());
2861  preparedStatement.executeUpdate();
2862  } catch (SQLException ex) {
2863  throw new CentralRepoException("Error inserting new reference instance into reference_ table.", ex); // NON-NLS
2864  } finally {
2865  CentralRepoDbUtil.closeStatement(preparedStatement);
2866  CentralRepoDbUtil.closeConnection(conn);
2867  }
2868  }
2869 
2882  @Override
2883  public boolean referenceSetExists(String referenceSetName, String version) throws CentralRepoException {
2884  Connection conn = connect();
2885 
2886  PreparedStatement preparedStatement1 = null;
2887  ResultSet resultSet = null;
2888  String sql1 = "SELECT * FROM reference_sets WHERE set_name=? AND version=?";
2889 
2890  try {
2891  preparedStatement1 = conn.prepareStatement(sql1);
2892  preparedStatement1.setString(1, referenceSetName);
2893  preparedStatement1.setString(2, version);
2894  resultSet = preparedStatement1.executeQuery();
2895  return (resultSet.next());
2896 
2897  } catch (SQLException ex) {
2898  throw new CentralRepoException("Error testing whether reference set exists (name: " + referenceSetName
2899  + " version: " + version, ex); // NON-NLS
2900  } finally {
2901  CentralRepoDbUtil.closeStatement(preparedStatement1);
2902  CentralRepoDbUtil.closeResultSet(resultSet);
2903  CentralRepoDbUtil.closeConnection(conn);
2904  }
2905  }
2906 
2912  @Override
2913  public void bulkInsertReferenceTypeEntries(Set<CentralRepoFileInstance> globalInstances, CorrelationAttributeInstance.Type contentType) throws CentralRepoException {
2914  if (contentType == null) {
2915  throw new CentralRepoException("Correlation type is null");
2916  }
2917  if (globalInstances == null) {
2918  throw new CentralRepoException("Null set of EamGlobalFileInstance");
2919  }
2920 
2921  Connection conn = connect();
2922 
2923  PreparedStatement bulkPs = null;
2924  try {
2925  conn.setAutoCommit(false);
2926 
2927  // FUTURE: have a separate global_files table for each Type.
2928  String sql = "INSERT INTO %s(reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) "
2929  + getConflictClause();
2930 
2931  bulkPs = conn.prepareStatement(String.format(sql, CentralRepoDbUtil.correlationTypeToReferenceTableName(contentType)));
2932 
2933  for (CentralRepoFileInstance globalInstance : globalInstances) {
2934  if (globalInstance.getKnownStatus() == null) {
2935  throw new CentralRepoException("EamGlobalFileInstance with value " + globalInstance.getMD5Hash() + " has null known status");
2936  }
2937 
2938  bulkPs.setInt(1, globalInstance.getGlobalSetID());
2939  bulkPs.setString(2, globalInstance.getMD5Hash());
2940  bulkPs.setByte(3, globalInstance.getKnownStatus().getFileKnownValue());
2941  bulkPs.setString(4, globalInstance.getComment());
2942  bulkPs.addBatch();
2943  }
2944 
2945  bulkPs.executeBatch();
2946  conn.commit();
2947  } catch (SQLException | CentralRepoException ex) {
2948  try {
2949  conn.rollback();
2950  } catch (SQLException ex2) {
2951  // We're alredy in an error state
2952  }
2953  throw new CentralRepoException("Error inserting bulk artifacts.", ex); // NON-NLS
2954  } finally {
2955  CentralRepoDbUtil.closeStatement(bulkPs);
2956  CentralRepoDbUtil.closeConnection(conn);
2957  }
2958  }
2959 
2970  @Override
2971  public List<CentralRepoFileInstance> getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws CentralRepoException, CorrelationAttributeNormalizationException {
2972  String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, aValue);
2973 
2974  Connection conn = connect();
2975 
2976  List<CentralRepoFileInstance> globalFileInstances = new ArrayList<>();
2977  PreparedStatement preparedStatement1 = null;
2978  ResultSet resultSet = null;
2979  String sql1 = "SELECT * FROM %s WHERE value=?";
2980 
2981  try {
2982  preparedStatement1 = conn.prepareStatement(String.format(sql1, CentralRepoDbUtil.correlationTypeToReferenceTableName(aType)));
2983  preparedStatement1.setString(1, normalizeValued);
2984  resultSet = preparedStatement1.executeQuery();
2985  while (resultSet.next()) {
2986  globalFileInstances.add(getEamGlobalFileInstanceFromResultSet(resultSet));
2987  }
2988 
2989  } catch (SQLException ex) {
2990  throw new CentralRepoException("Error getting reference instances by type and value.", ex); // NON-NLS
2991  } finally {
2992  CentralRepoDbUtil.closeStatement(preparedStatement1);
2993  CentralRepoDbUtil.closeResultSet(resultSet);
2994  CentralRepoDbUtil.closeConnection(conn);
2995  }
2996 
2997  return globalFileInstances;
2998  }
2999 
3009  @Override
3010  public int newCorrelationType(CorrelationAttributeInstance.Type newType) throws CentralRepoException {
3011  if (newType == null) {
3012  throw new CentralRepoException("Correlation type is null");
3013  }
3014  int typeId;
3015  if (-1 == newType.getId()) {
3016  typeId = newCorrelationTypeNotKnownId(newType);
3017  } else {
3018  typeId = newCorrelationTypeKnownId(newType);
3019  }
3020 
3021  synchronized(typeCache) {
3022  typeCache.put(newType.getId(), newType);
3023  }
3024  return typeId;
3025  }
3026 
3037  public int newCorrelationTypeNotKnownId(CorrelationAttributeInstance.Type newType) throws CentralRepoException {
3038  Connection conn = connect();
3039 
3040  PreparedStatement preparedStatement = null;
3041  PreparedStatement preparedStatementQuery = null;
3042  ResultSet resultSet = null;
3043  int typeId = 0;
3044  String insertSql;
3045  String querySql;
3046  // if we have a known ID, use it, if not (is -1) let the db assign it.
3047  insertSql = "INSERT INTO correlation_types(display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?) " + getConflictClause();
3048 
3049  querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?";
3050 
3051  try {
3052  preparedStatement = conn.prepareStatement(insertSql);
3053 
3054  preparedStatement.setString(1, newType.getDisplayName());
3055  preparedStatement.setString(2, newType.getDbTableName());
3056  preparedStatement.setInt(3, newType.isSupported() ? 1 : 0);
3057  preparedStatement.setInt(4, newType.isEnabled() ? 1 : 0);
3058 
3059  preparedStatement.executeUpdate();
3060 
3061  preparedStatementQuery = conn.prepareStatement(querySql);
3062  preparedStatementQuery.setString(1, newType.getDisplayName());
3063  preparedStatementQuery.setString(2, newType.getDbTableName());
3064 
3065  resultSet = preparedStatementQuery.executeQuery();
3066  if (resultSet.next()) {
3067  CorrelationAttributeInstance.Type correlationType = getCorrelationTypeFromResultSet(resultSet);
3068  typeId = correlationType.getId();
3069  }
3070  } catch (SQLException ex) {
3071  throw new CentralRepoException("Error inserting new correlation type.", ex); // NON-NLS
3072  } finally {
3073  CentralRepoDbUtil.closeStatement(preparedStatement);
3074  CentralRepoDbUtil.closeStatement(preparedStatementQuery);
3075  CentralRepoDbUtil.closeResultSet(resultSet);
3076  CentralRepoDbUtil.closeConnection(conn);
3077  }
3078  return typeId;
3079  }
3080 
3090  private int newCorrelationTypeKnownId(CorrelationAttributeInstance.Type newType) throws CentralRepoException {
3091  Connection conn = connect();
3092 
3093  PreparedStatement preparedStatement = null;
3094  PreparedStatement preparedStatementQuery = null;
3095  ResultSet resultSet = null;
3096  int typeId = 0;
3097  String insertSql;
3098  String querySql;
3099  // if we have a known ID, use it, if not (is -1) let the db assign it.
3100  insertSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause();
3101 
3102  querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?";
3103 
3104  try {
3105  preparedStatement = conn.prepareStatement(insertSql);
3106 
3107  preparedStatement.setInt(1, newType.getId());
3108  preparedStatement.setString(2, newType.getDisplayName());
3109  preparedStatement.setString(3, newType.getDbTableName());
3110  preparedStatement.setInt(4, newType.isSupported() ? 1 : 0);
3111  preparedStatement.setInt(5, newType.isEnabled() ? 1 : 0);
3112 
3113  preparedStatement.executeUpdate();
3114 
3115  preparedStatementQuery = conn.prepareStatement(querySql);
3116  preparedStatementQuery.setString(1, newType.getDisplayName());
3117  preparedStatementQuery.setString(2, newType.getDbTableName());
3118 
3119  resultSet = preparedStatementQuery.executeQuery();
3120  if (resultSet.next()) {
3121  CorrelationAttributeInstance.Type correlationType = getCorrelationTypeFromResultSet(resultSet);
3122  typeId = correlationType.getId();
3123  }
3124  } catch (SQLException ex) {
3125  throw new CentralRepoException("Error inserting new correlation type.", ex); // NON-NLS
3126  } finally {
3127  CentralRepoDbUtil.closeStatement(preparedStatement);
3128  CentralRepoDbUtil.closeStatement(preparedStatementQuery);
3129  CentralRepoDbUtil.closeResultSet(resultSet);
3130  CentralRepoDbUtil.closeConnection(conn);
3131  }
3132  return typeId;
3133  }
3134 
3135  @Override
3136  public List<CorrelationAttributeInstance.Type> getDefinedCorrelationTypes() throws CentralRepoException {
3137 
3138  synchronized (typeCache) {
3139  if (isCRTypeCacheInitialized == false) {
3140  getCorrelationTypesFromCr();
3141  }
3142  return new ArrayList<>(typeCache.asMap().values());
3143  }
3144  }
3145 
3155  @Override
3156  public List<CorrelationAttributeInstance.Type> getEnabledCorrelationTypes() throws CentralRepoException {
3157  Connection conn = connect();
3158 
3159  List<CorrelationAttributeInstance.Type> aTypes = new ArrayList<>();
3160  PreparedStatement preparedStatement = null;
3161  ResultSet resultSet = null;
3162  String sql = "SELECT * FROM correlation_types WHERE enabled=1";
3163 
3164  try {
3165  preparedStatement = conn.prepareStatement(sql);
3166  resultSet = preparedStatement.executeQuery();
3167  while (resultSet.next()) {
3168  aTypes.add(getCorrelationTypeFromResultSet(resultSet));
3169  }
3170  return aTypes;
3171 
3172  } catch (SQLException ex) {
3173  throw new CentralRepoException("Error getting enabled correlation types.", ex); // NON-NLS
3174  } finally {
3175  CentralRepoDbUtil.closeStatement(preparedStatement);
3176  CentralRepoDbUtil.closeResultSet(resultSet);
3177  CentralRepoDbUtil.closeConnection(conn);
3178  }
3179  }
3180 
3190  @Override
3191  public List<CorrelationAttributeInstance.Type> getSupportedCorrelationTypes() throws CentralRepoException {
3192  Connection conn = connect();
3193 
3194  List<CorrelationAttributeInstance.Type> aTypes = new ArrayList<>();
3195  PreparedStatement preparedStatement = null;
3196  ResultSet resultSet = null;
3197  String sql = "SELECT * FROM correlation_types WHERE supported=1";
3198 
3199  try {
3200  preparedStatement = conn.prepareStatement(sql);
3201  resultSet = preparedStatement.executeQuery();
3202  while (resultSet.next()) {
3203  aTypes.add(getCorrelationTypeFromResultSet(resultSet));
3204  }
3205  return aTypes;
3206 
3207  } catch (SQLException ex) {
3208  throw new CentralRepoException("Error getting supported correlation types.", ex); // NON-NLS
3209  } finally {
3210  CentralRepoDbUtil.closeStatement(preparedStatement);
3211  CentralRepoDbUtil.closeResultSet(resultSet);
3212  CentralRepoDbUtil.closeConnection(conn);
3213  }
3214  }
3215 
3223  @Override
3224  public void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws CentralRepoException {
3225  Connection conn = connect();
3226 
3227  PreparedStatement preparedStatement = null;
3228  String sql = "UPDATE correlation_types SET display_name=?, db_table_name=?, supported=?, enabled=? WHERE id=?";
3229 
3230  try {
3231  preparedStatement = conn.prepareStatement(sql);
3232  preparedStatement.setString(1, aType.getDisplayName());
3233  preparedStatement.setString(2, aType.getDbTableName());
3234  preparedStatement.setInt(3, aType.isSupported() ? 1 : 0);
3235  preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0);
3236  preparedStatement.setInt(5, aType.getId());
3237  preparedStatement.executeUpdate();
3238  synchronized(typeCache) {
3239  typeCache.put(aType.getId(), aType);
3240  }
3241  } catch (SQLException ex) {
3242  throw new CentralRepoException("Error updating correlation type.", ex); // NON-NLS
3243  } finally {
3244  CentralRepoDbUtil.closeStatement(preparedStatement);
3245  CentralRepoDbUtil.closeConnection(conn);
3246  }
3247 
3248  }
3249 
3259  @Override
3260  public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws CentralRepoException {
3261  try {
3262  synchronized(typeCache) {
3263  return typeCache.get(typeId, () -> getCorrelationTypeByIdFromCr(typeId));
3264  }
3265  } catch (CacheLoader.InvalidCacheLoadException ignored) {
3266  //lambda valueloader returned a null value and cache can not store null values this is normal if the correlation type does not exist in the central repo yet
3267  return null;
3268  } catch (ExecutionException ex) {
3269  throw new CentralRepoException("Error getting correlation type", ex);
3270  }
3271  }
3272 
3273 
3283  private CorrelationAttributeInstance.Type getCorrelationTypeByIdFromCr(int typeId) throws CentralRepoException {
3284  Connection conn = connect();
3285 
3286  CorrelationAttributeInstance.Type aType;
3287  PreparedStatement preparedStatement = null;
3288  ResultSet resultSet = null;
3289  String sql = "SELECT * FROM correlation_types WHERE id=?";
3290 
3291  try {
3292  preparedStatement = conn.prepareStatement(sql);
3293  preparedStatement.setInt(1, typeId);
3294  resultSet = preparedStatement.executeQuery();
3295  if (resultSet.next()) {
3296  aType = getCorrelationTypeFromResultSet(resultSet);
3297  return aType;
3298  } else {
3299  throw new CentralRepoException("Failed to find entry for correlation type ID = " + typeId);
3300  }
3301 
3302  } catch (SQLException ex) {
3303  throw new CentralRepoException("Error getting correlation type by id.", ex); // NON-NLS
3304  } finally {
3305  CentralRepoDbUtil.closeStatement(preparedStatement);
3306  CentralRepoDbUtil.closeResultSet(resultSet);
3307  CentralRepoDbUtil.closeConnection(conn);
3308  }
3309  }
3310 
3316  private void getCorrelationTypesFromCr() throws CentralRepoException {
3317 
3318  // clear out the cache
3319  synchronized(typeCache) {
3320  typeCache.invalidateAll();
3321  isCRTypeCacheInitialized = false;
3322  }
3323 
3324  String sql = "SELECT * FROM correlation_types";
3325  try ( Connection conn = connect();
3326  PreparedStatement preparedStatement = conn.prepareStatement(sql);
3327  ResultSet resultSet = preparedStatement.executeQuery();) {
3328 
3329  synchronized(typeCache) {
3330  while (resultSet.next()) {
3331  CorrelationAttributeInstance.Type aType = getCorrelationTypeFromResultSet(resultSet);
3332  typeCache.put(aType.getId(), aType);
3333  }
3334  isCRTypeCacheInitialized = true;
3335  }
3336  } catch (SQLException ex) {
3337  throw new CentralRepoException("Error getting correlation types.", ex); // NON-NLS
3338  }
3339  }
3340 
3351  private CorrelationCase getEamCaseFromResultSet(ResultSet resultSet) throws SQLException {
3352  if (null == resultSet) {
3353  return null;
3354  }
3355 
3356  CentralRepoOrganization eamOrg = null;
3357 
3358  resultSet.getInt("org_id");
3359  if (!resultSet.wasNull()) {
3360 
3361  eamOrg = new CentralRepoOrganization(resultSet.getInt("org_id"),
3362  resultSet.getString("org_name"),
3363  resultSet.getString("poc_name"),
3364  resultSet.getString("poc_email"),
3365  resultSet.getString("poc_phone"));
3366  }
3367 
3368  CorrelationCase eamCase = new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), eamOrg, resultSet.getString("case_name"),
3369  resultSet.getString("creation_date"), resultSet.getString("case_number"), resultSet.getString("examiner_name"),
3370  resultSet.getString("examiner_email"), resultSet.getString("examiner_phone"), resultSet.getString("notes"));
3371 
3372  return eamCase;
3373  }
3374 
3375  private CorrelationDataSource getEamDataSourceFromResultSet(ResultSet resultSet) throws SQLException {
3376  if (null == resultSet) {
3377  return null;
3378  }
3379 
3380  CorrelationDataSource eamDataSource = new CorrelationDataSource(
3381  resultSet.getInt("case_id"),
3382  resultSet.getInt("id"),
3383  resultSet.getString("device_id"),
3384  resultSet.getString("name"),
3385  resultSet.getLong("datasource_obj_id"),
3386  resultSet.getString("md5"),
3387  resultSet.getString("sha1"),
3388  resultSet.getString("sha256")
3389  );
3390 
3391  return eamDataSource;
3392  }
3393 
3394  private CorrelationAttributeInstance.Type getCorrelationTypeFromResultSet(ResultSet resultSet) throws CentralRepoException, SQLException {
3395  if (null == resultSet) {
3396  return null;
3397  }
3398 
3399  CorrelationAttributeInstance.Type eamArtifactType = new CorrelationAttributeInstance.Type(
3400  resultSet.getInt("id"),
3401  resultSet.getString("display_name"),
3402  resultSet.getString("db_table_name"),
3403  resultSet.getBoolean("supported"),
3404  resultSet.getBoolean("enabled")
3405  );
3406 
3407  return eamArtifactType;
3408  }
3409 
3420  private CorrelationAttributeInstance getEamArtifactInstanceFromResultSet(ResultSet resultSet, CorrelationAttributeInstance.Type aType) throws SQLException, CentralRepoException, CorrelationAttributeNormalizationException {
3421  if (null == resultSet) {
3422  return null;
3423  }
3424 
3425  CentralRepoOrganization eamOrg = new CentralRepoOrganization(resultSet.getInt("org_id"),
3426  resultSet.getString("org_name"),
3427  resultSet.getString("poc_name"),
3428  resultSet.getString("poc_email"),
3429  resultSet.getString("poc_phone"));
3430 
3431  return new CorrelationAttributeInstance(
3432  aType,
3433  resultSet.getString("value"),
3434  resultSet.getInt("instance_id"),
3435  new CorrelationCase(resultSet.getInt("id"), resultSet.getString("case_uid"), eamOrg, resultSet.getString("case_name"),
3436  resultSet.getString("creation_date"), resultSet.getString("case_number"), resultSet.getString("examiner_name"),
3437  resultSet.getString("examiner_email"), resultSet.getString("examiner_phone"), resultSet.getString("notes")),
3438  new CorrelationDataSource(
3439  resultSet.getInt("id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name"),
3440  resultSet.getLong("datasource_obj_id"), resultSet.getString("md5"), resultSet.getString("sha1"), resultSet.getString("sha256")),
3441  resultSet.getString("file_path"),
3442  resultSet.getString("comment"),
3443  TskData.FileKnown.valueOf(resultSet.getByte("known_status")),
3444  resultSet.getLong("file_obj_id"));
3445  }
3446 
3447  private CentralRepoOrganization getEamOrganizationFromResultSet(ResultSet resultSet) throws SQLException {
3448  if (null == resultSet) {
3449  return null;
3450  }
3451 
3452  return new CentralRepoOrganization(
3453  resultSet.getInt("id"),
3454  resultSet.getString("org_name"),
3455  resultSet.getString("poc_name"),
3456  resultSet.getString("poc_email"),
3457  resultSet.getString("poc_phone")
3458  );
3459  }
3460 
3461  private CentralRepoFileSet getEamGlobalSetFromResultSet(ResultSet resultSet) throws SQLException, CentralRepoException {
3462  if (null == resultSet) {
3463  return null;
3464  }
3465 
3466  return new CentralRepoFileSet(
3467  resultSet.getInt("id"),
3468  resultSet.getInt("org_id"),
3469  resultSet.getString("set_name"),
3470  resultSet.getString("version"),
3471  TskData.FileKnown.valueOf(resultSet.getByte("known_status")),
3472  resultSet.getBoolean("read_only"),
3473  CentralRepository.getInstance().getCorrelationTypeById(resultSet.getInt("type")),
3474  LocalDate.parse(resultSet.getString("import_date"))
3475  );
3476  }
3477 
3478  private CentralRepoFileInstance getEamGlobalFileInstanceFromResultSet(ResultSet resultSet) throws SQLException, CentralRepoException, CorrelationAttributeNormalizationException {
3479  if (null == resultSet) {
3480  return null;
3481  }
3482 
3483  return new CentralRepoFileInstance(
3484  resultSet.getInt("id"),
3485  resultSet.getInt("reference_set_id"),
3486  resultSet.getString("value"),
3487  TskData.FileKnown.valueOf(resultSet.getByte("known_status")),
3488  resultSet.getString("comment")
3489  );
3490  }
3491 
3502  abstract boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException;
3503 
3509  @Messages({"AbstractSqlEamDb.upgradeSchema.incompatible=The selected Central Repository is not compatible with the current version of the application, please upgrade the application if you wish to use this Central Repository.",
3510  "# {0} - minorVersion",
3511  "AbstractSqlEamDb.badMinorSchema.message=Bad value for schema minor version ({0}) - database is corrupt.",
3512  "AbstractSqlEamDb.failedToReadMinorVersion.message=Failed to read schema minor version for Central Repository.",
3513  "# {0} - majorVersion",
3514  "AbstractSqlEamDb.badMajorSchema.message=Bad value for schema version ({0}) - database is corrupt.",
3515  "AbstractSqlEamDb.failedToReadMajorVersion.message=Failed to read schema version for Central Repository.",
3516  "# {0} - platformName",
3517  "AbstractSqlEamDb.cannotUpgrage.message=Currently selected database platform \"{0}\" can not be upgraded."})
3518  @Override
3519  public void upgradeSchema() throws CentralRepoException, SQLException, IncompatibleCentralRepoException {
3520 
3521  ResultSet resultSet = null;
3522  Statement statement = null;
3523  PreparedStatement preparedStatement = null;
3524  Connection conn = null;
3525  CentralRepoPlatforms selectedPlatform = null;
3526  try {
3527 
3528  conn = connect(false);
3529  conn.setAutoCommit(false);
3530  statement = conn.createStatement();
3531  selectedPlatform = CentralRepoDbManager.getSavedDbChoice().getDbPlatform();
3532  int minorVersion = 0;
3533  String minorVersionStr = null;
3534  resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + RdbmsCentralRepo.SCHEMA_MINOR_VERSION_KEY + "'");
3535  if (resultSet.next()) {
3536  minorVersionStr = resultSet.getString("value");
3537  try {
3538  minorVersion = Integer.parseInt(minorVersionStr);
3539  } catch (NumberFormatException ex) {
3540  throw new CentralRepoException("Bad value for schema minor version (" + minorVersionStr + ") - database is corrupt", Bundle.AbstractSqlEamDb_badMinorSchema_message(minorVersionStr), ex);
3541  }
3542  } else {
3543  throw new CentralRepoException("Failed to read schema minor version from db_info table", Bundle.AbstractSqlEamDb_failedToReadMinorVersion_message());
3544  }
3545 
3546  int majorVersion = 0;
3547  String majorVersionStr = null;
3548  resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + RdbmsCentralRepo.SCHEMA_MAJOR_VERSION_KEY + "'");
3549  if (resultSet.next()) {
3550  majorVersionStr = resultSet.getString("value");
3551  try {
3552  majorVersion = Integer.parseInt(majorVersionStr);
3553  } catch (NumberFormatException ex) {
3554  throw new CentralRepoException("Bad value for schema version (" + majorVersionStr + ") - database is corrupt", Bundle.AbstractSqlEamDb_badMajorSchema_message(majorVersionStr), ex);
3555  }
3556  } else {
3557  throw new CentralRepoException("Failed to read schema major version from db_info table", Bundle.AbstractSqlEamDb_failedToReadMajorVersion_message());
3558  }
3559 
3560  /*
3561  * IMPORTANT: The code that follows had a bug in it prior to Autopsy
3562  * 4.10.0. The consequence of the bug is that the schema version
3563  * number is always reset to 1.0 or 1.1 if a Central Repository is
3564  * opened by an Autopsy 4.9.1 or earlier client. To cope with this,
3565  * there is an effort in updates to 1.2 and greater to not retry
3566  * schema updates that may already have been done once.
3567  */
3568  CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(majorVersion, minorVersion);
3569 
3570  //compare the major versions for compatability
3571  //we can not use the CaseDbSchemaVersionNumber.isCompatible method
3572  //because it is specific to case db schema versions only supporting major versions greater than 1
3573  if (SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() < dbSchemaVersion.getMajor()) {
3574  throw new IncompatibleCentralRepoException(Bundle.AbstractSqlEamDb_upgradeSchema_incompatible());
3575  }
3576  if (dbSchemaVersion.equals(SOFTWARE_CR_DB_SCHEMA_VERSION)) {
3577  logger.log(Level.INFO, "Central Repository is up to date");
3578  return;
3579  }
3580  if (dbSchemaVersion.compareTo(SOFTWARE_CR_DB_SCHEMA_VERSION) > 0) {
3581  logger.log(Level.INFO, "Central Repository is of newer version than software creates");
3582  return;
3583  }
3584 
3585  /*
3586  * Update to 1.1
3587  */
3588  if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) {
3589  statement.execute("ALTER TABLE reference_sets ADD COLUMN known_status INTEGER;"); //NON-NLS
3590  statement.execute("ALTER TABLE reference_sets ADD COLUMN read_only BOOLEAN;"); //NON-NLS
3591  statement.execute("ALTER TABLE reference_sets ADD COLUMN type INTEGER;"); //NON-NLS
3592 
3593  // There's an outide chance that the user has already made an organization with the default name,
3594  // and the default org being missing will not impact any database operations, so continue on
3595  // regardless of whether this succeeds.
3596  CentralRepoDbUtil.insertDefaultOrganization(conn);
3597  }
3598 
3599  /*
3600  * Update to 1.2
3601  */
3602  if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
3603  final String addIntegerColumnTemplate = "ALTER TABLE %s ADD COLUMN %s INTEGER;"; //NON-NLS
3604 
3605  final String addSsidTableTemplate = RdbmsCentralRepoFactory.getCreateArtifactInstancesTableTemplate(selectedPlatform);
3606  final String addCaseIdIndexTemplate = RdbmsCentralRepoFactory.getAddCaseIdIndexTemplate();
3607  final String addDataSourceIdIndexTemplate = RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate();
3608  final String addValueIndexTemplate = RdbmsCentralRepoFactory.getAddValueIndexTemplate();
3609  final String addKnownStatusIndexTemplate = RdbmsCentralRepoFactory.getAddKnownStatusIndexTemplate();
3610  final String addObjectIdIndexTemplate = RdbmsCentralRepoFactory.getAddObjectIdIndexTemplate();
3611 
3612  final String addAttributeSql;
3613  //get the data base specific code for creating a new _instance table
3614  switch (selectedPlatform) {
3615  case POSTGRESQL:
3616  addAttributeSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); //NON-NLS
3617  break;
3618  case SQLITE:
3619  addAttributeSql = "INSERT OR IGNORE INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)"; //NON-NLS
3620  break;
3621  default:
3622  throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name()));
3623  }
3624 
3625  final String dataSourcesTableName = "data_sources";
3626  final String dataSourceObjectIdColumnName = "datasource_obj_id";
3627  if (!doesColumnExist(conn, dataSourcesTableName, dataSourceObjectIdColumnName)) {
3628  statement.execute(String.format(addIntegerColumnTemplate, dataSourcesTableName, dataSourceObjectIdColumnName)); //NON-NLS
3629  }
3630  final String dataSourceObjectIdIndexTemplate = "CREATE INDEX IF NOT EXISTS datasource_object_id ON data_sources (%s)";
3631  statement.execute(String.format(dataSourceObjectIdIndexTemplate, dataSourceObjectIdColumnName));
3632  List<String> instaceTablesToAdd = new ArrayList<>();
3633  //update central repository to be able to store new correlation attributes
3634  final String wirelessNetworksDbTableName = "wireless_networks";
3635  instaceTablesToAdd.add(wirelessNetworksDbTableName + "_instances");
3636  final String macAddressDbTableName = "mac_address";
3637  instaceTablesToAdd.add(macAddressDbTableName + "_instances");
3638  final String imeiNumberDbTableName = "imei_number";
3639  instaceTablesToAdd.add(imeiNumberDbTableName + "_instances");
3640  final String iccidNumberDbTableName = "iccid_number";
3641  instaceTablesToAdd.add(iccidNumberDbTableName + "_instances");
3642  final String imsiNumberDbTableName = "imsi_number";
3643  instaceTablesToAdd.add(imsiNumberDbTableName + "_instances");
3644 
3645  //add the wireless_networks attribute to the correlation_types table
3646  preparedStatement = conn.prepareStatement(addAttributeSql);
3647  preparedStatement.setInt(1, CorrelationAttributeInstance.SSID_TYPE_ID);
3648  preparedStatement.setString(2, Bundle.CorrelationType_SSID_displayName());
3649  preparedStatement.setString(3, wirelessNetworksDbTableName);
3650  preparedStatement.setInt(4, 1);
3651  preparedStatement.setInt(5, 1);
3652  preparedStatement.execute();
3653 
3654  //add the mac_address attribute to the correlation_types table
3655  preparedStatement = conn.prepareStatement(addAttributeSql);
3656  preparedStatement.setInt(1, CorrelationAttributeInstance.MAC_TYPE_ID);
3657  preparedStatement.setString(2, Bundle.CorrelationType_MAC_displayName());
3658  preparedStatement.setString(3, macAddressDbTableName);
3659  preparedStatement.setInt(4, 1);
3660  preparedStatement.setInt(5, 1);
3661  preparedStatement.execute();
3662 
3663  //add the imei_number attribute to the correlation_types table
3664  preparedStatement = conn.prepareStatement(addAttributeSql);
3665  preparedStatement.setInt(1, CorrelationAttributeInstance.IMEI_TYPE_ID);
3666  preparedStatement.setString(2, Bundle.CorrelationType_IMEI_displayName());
3667  preparedStatement.setString(3, imeiNumberDbTableName);
3668  preparedStatement.setInt(4, 1);
3669  preparedStatement.setInt(5, 1);
3670  preparedStatement.execute();
3671 
3672  //add the imsi_number attribute to the correlation_types table
3673  preparedStatement = conn.prepareStatement(addAttributeSql);
3674  preparedStatement.setInt(1, CorrelationAttributeInstance.IMSI_TYPE_ID);
3675  preparedStatement.setString(2, Bundle.CorrelationType_IMSI_displayName());
3676  preparedStatement.setString(3, imsiNumberDbTableName);
3677  preparedStatement.setInt(4, 1);
3678  preparedStatement.setInt(5, 1);
3679  preparedStatement.execute();
3680 
3681  //add the iccid_number attribute to the correlation_types table
3682  preparedStatement = conn.prepareStatement(addAttributeSql);
3683  preparedStatement.setInt(1, CorrelationAttributeInstance.ICCID_TYPE_ID);
3684  preparedStatement.setString(2, Bundle.CorrelationType_ICCID_displayName());
3685  preparedStatement.setString(3, iccidNumberDbTableName);
3686  preparedStatement.setInt(4, 1);
3687  preparedStatement.setInt(5, 1);
3688  preparedStatement.execute();
3689 
3690  //create a new _instances tables and add indexes for their columns
3691  for (String tableName : instaceTablesToAdd) {
3692  statement.execute(String.format(addSsidTableTemplate, tableName, tableName));
3693  statement.execute(String.format(addCaseIdIndexTemplate, tableName, tableName));
3694  statement.execute(String.format(addDataSourceIdIndexTemplate, tableName, tableName));
3695  statement.execute(String.format(addValueIndexTemplate, tableName, tableName));
3696  statement.execute(String.format(addKnownStatusIndexTemplate, tableName, tableName));
3697  }
3698 
3699  //add file_obj_id column to _instances table which do not already have it
3700  String instance_type_dbname;
3701  final String objectIdColumnName = "file_obj_id";
3702  for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) {
3703  instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
3704  if (!doesColumnExist(conn, instance_type_dbname, objectIdColumnName)) {
3705  statement.execute(String.format(addIntegerColumnTemplate, instance_type_dbname, objectIdColumnName)); //NON-NLS
3706  }
3707  statement.execute(String.format(addObjectIdIndexTemplate, instance_type_dbname, instance_type_dbname));
3708  }
3709 
3710  /*
3711  * Add hash columns to the data_sources table.
3712  */
3713  if (!doesColumnExist(conn, dataSourcesTableName, "md5")) {
3714  statement.execute("ALTER TABLE data_sources ADD COLUMN md5 TEXT DEFAULT NULL");
3715  }
3716  if (!doesColumnExist(conn, dataSourcesTableName, "sha1")) {
3717  statement.execute("ALTER TABLE data_sources ADD COLUMN sha1 TEXT DEFAULT NULL");
3718  }
3719  if (!doesColumnExist(conn, dataSourcesTableName, "sha256")) {
3720  statement.execute("ALTER TABLE data_sources ADD COLUMN sha256 TEXT DEFAULT NULL");
3721  }
3722 
3723  /*
3724  * Drop the db_info table and add it back in with the name
3725  * column having a UNIQUE constraint. The name column could now
3726  * be used as the primary key, but the essentially useless id
3727  * column is retained for the sake of backwards compatibility.
3728  * Note that the creation schema version number is set to 0.0 to
3729  * indicate that it is unknown.
3730  */
3731  String creationMajorVer;
3732  resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name = '" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "'");
3733  if (resultSet.next()) {
3734  creationMajorVer = resultSet.getString("value");
3735  } else {
3736  creationMajorVer = "0";
3737  }
3738  String creationMinorVer;
3739  resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name = '" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "'");
3740  if (resultSet.next()) {
3741  creationMinorVer = resultSet.getString("value");
3742  } else {
3743  creationMinorVer = "0";
3744  }
3745  statement.execute("DROP TABLE db_info");
3746  if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL) {
3747  statement.execute("CREATE TABLE db_info (id SERIAL, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)");
3748  } else {
3749  statement.execute("CREATE TABLE db_info (id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)");
3750  }
3751  statement.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.SCHEMA_MAJOR_VERSION_KEY + "','" + majorVersionStr + "')");
3752  statement.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.SCHEMA_MINOR_VERSION_KEY + "','" + minorVersionStr + "')");
3753  statement.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "','" + creationMajorVer + "')");
3754  statement.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "','" + creationMinorVer + "')");
3755  }
3756  /*
3757  * Update to 1.3
3758  */
3759  if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 3)) < 0) {
3760  switch (selectedPlatform) {
3761  case POSTGRESQL:
3762  statement.execute("ALTER TABLE data_sources DROP CONSTRAINT datasource_unique");
3763  //unique constraint for upgraded data_sources table is purposefully different than new data_sources table
3764  statement.execute("ALTER TABLE data_sources ADD CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name, datasource_obj_id)");
3765 
3766  break;
3767  case SQLITE:
3768  statement.execute("DROP INDEX IF EXISTS data_sources_name");
3769  statement.execute("DROP INDEX IF EXISTS data_sources_object_id");
3770  statement.execute("ALTER TABLE data_sources RENAME TO old_data_sources");
3771  //unique constraint for upgraded data_sources table is purposefully different than new data_sources table
3772  statement.execute("CREATE TABLE IF NOT EXISTS data_sources (id integer primary key autoincrement NOT NULL,"
3773  + "case_id integer NOT NULL,device_id text NOT NULL,name text NOT NULL,datasource_obj_id integer,"
3774  + "md5 text DEFAULT NULL,sha1 text DEFAULT NULL,sha256 text DEFAULT NULL,"
3775  + "foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"
3776  + "CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name, datasource_obj_id))");
3777  statement.execute(RdbmsCentralRepoFactory.getAddDataSourcesNameIndexStatement());
3778  statement.execute(RdbmsCentralRepoFactory.getAddDataSourcesObjectIdIndexStatement());
3779  statement.execute("INSERT INTO data_sources SELECT * FROM old_data_sources");
3780  statement.execute("DROP TABLE old_data_sources");
3781  break;
3782  default:
3783  throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name()));
3784  }
3785  }
3786 
3787  // Upgrade to 1.4
3788  (new CentralRepoDbUpgrader13To14()).upgradeSchema(dbSchemaVersion, conn);
3789 
3790  updateSchemaVersion(conn);
3791  conn.commit();
3792  logger.log(Level.INFO, String.format("Central Repository schema updated to version %s", SOFTWARE_CR_DB_SCHEMA_VERSION));
3793  } catch (SQLException | CentralRepoException ex) {
3794  try {
3795  if (conn != null) {
3796  conn.rollback();
3797  }
3798  } catch (SQLException ex2) {
3799  logger.log(Level.SEVERE, String.format("Central Repository rollback of failed schema update to %s failed", SOFTWARE_CR_DB_SCHEMA_VERSION), ex2);
3800  }
3801  throw ex;
3802  } finally {
3803  CentralRepoDbUtil.closeResultSet(resultSet);
3804  CentralRepoDbUtil.closeStatement(preparedStatement);
3805  CentralRepoDbUtil.closeStatement(statement);
3806  CentralRepoDbUtil.closeConnection(conn);
3807  }
3808  }
3809 
3810 }
CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID)

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.