19 package org.sleuthkit.autopsy.centralrepository.datamodel;
21 import java.sql.Connection;
22 import java.sql.DriverManager;
23 import java.sql.PreparedStatement;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.Properties;
31 import java.util.logging.Level;
32 import java.util.regex.Pattern;
70 if (host == null || host.isEmpty()) {
76 if (portString == null || portString.isEmpty()) {
79 port = Integer.parseInt(portString);
80 if (port < 0 || port > 65535) {
84 }
catch (NumberFormatException ex) {
89 if (dbName == null || dbName.isEmpty()) {
95 if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
98 this.bulkThreshold = Integer.parseInt(bulkThresholdString);
103 }
catch (NumberFormatException ex) {
108 if (userName == null || userName.isEmpty()) {
113 if (password == null || password.isEmpty()) {
119 LOGGER.log(Level.WARNING,
"Failed to convert password from hex text to text.", ex);
125 if (badTagsStr == null) {
128 if(badTagsStr.isEmpty()){
129 badTags =
new ArrayList<>();
131 badTags =
new ArrayList<>(Arrays.asList(badTagsStr.split(
",")));
144 LOGGER.log(Level.SEVERE,
"Failed to convert password from text to hex text.", ex);
159 StringBuilder url =
new StringBuilder();
164 url.append(
"postgres");
169 return url.toString();
182 Properties props =
new Properties();
187 conn = DriverManager.getConnection(url, props);
188 }
catch (ClassNotFoundException | SQLException ex) {
191 LOGGER.log(Level.SEVERE,
"Failed to acquire ephemeral connection to postgresql.");
225 String sql =
"SELECT datname FROM pg_catalog.pg_database WHERE lower(datname) = lower(?) LIMIT 1";
226 PreparedStatement ps = null;
229 ps = conn.prepareStatement(sql);
231 rs = ps.executeQuery();
235 }
catch (SQLException ex) {
236 LOGGER.log(Level.SEVERE,
"Failed to execute database existance query.", ex);
270 String sql =
"CREATE DATABASE %s OWNER %s";
273 stmt = conn.createStatement();
275 }
catch (SQLException ex) {
276 LOGGER.log(Level.SEVERE,
"Failed to execute create database statement.", ex);
291 String sql =
"DROP DATABASE %s";
294 stmt = conn.createStatement();
295 stmt.execute(String.format(sql,
getDbName()));
296 }
catch (SQLException ex) {
297 LOGGER.log(Level.SEVERE,
"Failed to execute drop database statement.", ex);
323 StringBuilder createOrganizationsTable =
new StringBuilder();
324 createOrganizationsTable.append(
"CREATE TABLE IF NOT EXISTS organizations (");
325 createOrganizationsTable.append(
"id SERIAL PRIMARY KEY,");
326 createOrganizationsTable.append(
"org_name text NOT NULL,");
327 createOrganizationsTable.append(
"poc_name text NOT NULL,");
328 createOrganizationsTable.append(
"poc_email text NOT NULL,");
329 createOrganizationsTable.append(
"poc_phone text NOT NULL,");
330 createOrganizationsTable.append(
"CONSTRAINT org_name_unique UNIQUE (org_name)");
331 createOrganizationsTable.append(
")");
335 StringBuilder createCasesTable =
new StringBuilder();
336 createCasesTable.append(
"CREATE TABLE IF NOT EXISTS cases (");
337 createCasesTable.append(
"id SERIAL PRIMARY KEY,");
338 createCasesTable.append(
"case_uid text NOT NULL,");
339 createCasesTable.append(
"org_id integer,");
340 createCasesTable.append(
"case_name text NOT NULL,");
341 createCasesTable.append(
"creation_date text NOT NULL,");
342 createCasesTable.append(
"case_number text,");
343 createCasesTable.append(
"examiner_name text,");
344 createCasesTable.append(
"examiner_email text,");
345 createCasesTable.append(
"examiner_phone text,");
346 createCasesTable.append(
"notes text,");
347 createCasesTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL");
348 createCasesTable.append(
")");
351 String casesIdx1 =
"CREATE INDEX IF NOT EXISTS cases_org_id ON cases (org_id)";
352 String casesIdx2 =
"CREATE INDEX IF NOT EXISTS cases_case_uid ON cases (case_uid)";
354 StringBuilder createDataSourcesTable =
new StringBuilder();
355 createDataSourcesTable.append(
"CREATE TABLE IF NOT EXISTS data_sources (");
356 createDataSourcesTable.append(
"id SERIAL PRIMARY KEY,");
357 createDataSourcesTable.append(
"device_id text NOT NULL,");
358 createDataSourcesTable.append(
"name text NOT NULL,");
359 createDataSourcesTable.append(
"CONSTRAINT device_id_unique UNIQUE (device_id)");
360 createDataSourcesTable.append(
")");
362 String dataSourceIdx1 =
"CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)";
364 StringBuilder createReferenceSetsTable =
new StringBuilder();
365 createReferenceSetsTable.append(
"CREATE TABLE IF NOT EXISTS reference_sets (");
366 createReferenceSetsTable.append(
"id SERIAL PRIMARY KEY,");
367 createReferenceSetsTable.append(
"org_id integer NOT NULL,");
368 createReferenceSetsTable.append(
"set_name text NOT NULL,");
369 createReferenceSetsTable.append(
"version text NOT NULL,");
370 createReferenceSetsTable.append(
"import_date text NOT NULL,");
371 createReferenceSetsTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL,");
372 createReferenceSetsTable.append(
"CONSTRAINT hash_set_unique UNIQUE (set_name, version)");
373 createReferenceSetsTable.append(
")");
375 String referenceSetsIdx1 =
"CREATE INDEX IF NOT EXISTS reference_sets_org_id ON reference_sets (org_id)";
378 StringBuilder createReferenceTypesTableTemplate =
new StringBuilder();
379 createReferenceTypesTableTemplate.append(
"CREATE TABLE IF NOT EXISTS %s (");
380 createReferenceTypesTableTemplate.append(
"id SERIAL PRIMARY KEY,");
381 createReferenceTypesTableTemplate.append(
"reference_set_id integer,");
382 createReferenceTypesTableTemplate.append(
"value text NOT NULL,");
383 createReferenceTypesTableTemplate.append(
"known_status integer NOT NULL,");
384 createReferenceTypesTableTemplate.append(
"comment text,");
385 createReferenceTypesTableTemplate.append(
"CONSTRAINT %s_multi_unique UNIQUE (reference_set_id, value),");
386 createReferenceTypesTableTemplate.append(
"foreign key (reference_set_id) references reference_sets(id) ON UPDATE SET NULL ON DELETE SET NULL");
387 createReferenceTypesTableTemplate.append(
")");
390 String referenceTypesIdx1 =
"CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
391 String referenceTypesIdx2 =
"CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
393 StringBuilder createCorrelationTypesTable =
new StringBuilder();
394 createCorrelationTypesTable.append(
"CREATE TABLE IF NOT EXISTS correlation_types (");
395 createCorrelationTypesTable.append(
"id SERIAL PRIMARY KEY,");
396 createCorrelationTypesTable.append(
"display_name text NOT NULL,");
397 createCorrelationTypesTable.append(
"db_table_name text NOT NULL,");
398 createCorrelationTypesTable.append(
"supported integer NOT NULL,");
399 createCorrelationTypesTable.append(
"enabled integer NOT NULL,");
400 createCorrelationTypesTable.append(
"CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)");
401 createCorrelationTypesTable.append(
")");
404 StringBuilder createArtifactInstancesTableTemplate =
new StringBuilder();
405 createArtifactInstancesTableTemplate.append(
"CREATE TABLE IF NOT EXISTS %s (");
406 createArtifactInstancesTableTemplate.append(
"id SERIAL PRIMARY KEY,");
407 createArtifactInstancesTableTemplate.append(
"case_id integer,");
408 createArtifactInstancesTableTemplate.append(
"data_source_id integer,");
409 createArtifactInstancesTableTemplate.append(
"value text NOT NULL,");
410 createArtifactInstancesTableTemplate.append(
"file_path text NOT NULL,");
411 createArtifactInstancesTableTemplate.append(
"known_status integer NOT NULL,");
412 createArtifactInstancesTableTemplate.append(
"comment text,");
413 createArtifactInstancesTableTemplate.append(
"CONSTRAINT %s_multi_unique_ UNIQUE (case_id, data_source_id, value, file_path),");
414 createArtifactInstancesTableTemplate.append(
"foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
415 createArtifactInstancesTableTemplate.append(
"foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
416 createArtifactInstancesTableTemplate.append(
")");
419 String instancesIdx1 =
"CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
420 String instancesIdx2 =
"CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
421 String instancesIdx3 =
"CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
422 String instancesIdx4 =
"CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
424 StringBuilder createDbInfoTable =
new StringBuilder();
425 createDbInfoTable.append(
"CREATE TABLE IF NOT EXISTS db_info (");
426 createDbInfoTable.append(
"id SERIAL PRIMARY KEY NOT NULL,");
427 createDbInfoTable.append(
"name text NOT NULL,");
428 createDbInfoTable.append(
"value text NOT NULL");
429 createDbInfoTable.append(
")");
433 Connection conn = null;
439 Statement stmt = conn.createStatement();
441 stmt.execute(createOrganizationsTable.toString());
443 stmt.execute(createCasesTable.toString());
444 stmt.execute(casesIdx1);
445 stmt.execute(casesIdx2);
447 stmt.execute(createDataSourcesTable.toString());
448 stmt.execute(dataSourceIdx1);
450 stmt.execute(createReferenceSetsTable.toString());
451 stmt.execute(referenceSetsIdx1);
453 stmt.execute(createCorrelationTypesTable.toString());
455 stmt.execute(createDbInfoTable.toString());
460 String reference_type_dbname;
461 String instance_type_dbname;
466 stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname));
467 stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname));
468 stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname));
469 stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname));
470 stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname));
474 stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
475 stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
476 stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
480 }
catch (SQLException ex) {
481 LOGGER.log(Level.SEVERE,
"Error initializing db schema.", ex);
484 LOGGER.log(Level.SEVERE,
"Error getting default correlation types. Likely due to one or more Type's with an invalid db table name.");
513 return !host.equals(hostString) || !Integer.toString(port).equals(portString)
514 || !dbName.equals(dbNameString) || !Integer.toString(bulkThreshold).equals(bulkThresholdString)
515 || !userName.equals(userNameString) || !password.equals(userPasswordString);
529 if (null != host && !host.isEmpty()) {
547 if (port > 0 && port < 65535) {
550 throw new EamDbException(
"Invalid port. Must be a number greater than 0.");
561 return dbName.toLowerCase();
568 if (dbName == null || dbName.isEmpty()) {
569 throw new EamDbException(
"Invalid database name. Cannot be empty.");
570 }
else if (!Pattern.matches(DB_NAMES_REGEX, dbName)) {
571 throw new EamDbException(
"Invalid database name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.");
574 this.dbName = dbName.toLowerCase();
588 if (bulkThreshold > 0) {
606 if (userName == null || userName.isEmpty()) {
608 }
else if (!Pattern.matches(DB_USER_NAMES_REGEX, userName)) {
609 throw new EamDbException(
"Invalid user name. Name must start with a letter and can only contain letters, numbers, and '_'.");
625 if (password == null || password.isEmpty()) {
626 throw new EamDbException(
"Invalid user password. Cannot be empty.");
static final Logger LOGGER
static boolean insertSchemaVersion(Connection conn)
void setHost(String host)
Connection getEphemeralConnection(boolean usePostgresDb)
static boolean schemaVersionIsSet(Connection conn)
final String VALIDATION_QUERY
static boolean executeValidationQuery(Connection conn, String validationQuery)
final String DEFAULT_DBNAME
static final int FILES_TYPE_ID
boolean insertDefaultDatabaseContent()
boolean verifyDatabaseSchema()
final String JDBC_BASE_URI
final String DEFAULT_USERNAME
final String DEFAULT_BAD_TAGS
static void closeResultSet(ResultSet resultSet)
List< String > getBadTags()
final String DEFAULT_HOST
final String DB_NAMES_REGEX
static String correlationTypeToReferenceTableName(CorrelationAttribute.Type type)
void setPassword(String password)
final String DEFAULT_PASSWORD
static void closeConnection(Connection conn)
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
boolean initializeDatabaseSchema()
void setBadTags(List< String > badTags)
boolean verifyDatabaseExists()
final int DEFAULT_BULK_THRESHHOLD
String getValidationQuery()
static boolean insertDefaultCorrelationTypes(Connection conn)
String getConnectionURL(boolean usePostgresDb)
static String correlationTypeToInstanceTableName(CorrelationAttribute.Type type)
static String getConfigSetting(String moduleName, String settingName)
boolean verifyConnection()
void setUserName(String userName)
void setDbName(String dbName)
synchronized static Logger getLogger(String name)
static List< CorrelationAttribute.Type > getDefaultCorrelationTypes()
void setBulkThreshold(int bulkThreshold)
static String convertTextToHexText(String property)
static void closePreparedStatement(PreparedStatement preparedStatement)
final String DB_USER_NAMES_REGEX
static String convertHexTextToText(String property)