Remove pgcrypto checks

(cherry picked from commit 252512bdac)
This commit is contained in:
Francisco
2025-06-08 16:04:19 -03:00
committed by github-actions[bot]
parent 5d8472bd97
commit 94bcd3c0fb
4 changed files with 0 additions and 516 deletions

View File

@@ -123,16 +123,6 @@ public class DatabaseUtils {
// Print basic database connection information
printDBInfo(connection);
// Print any database warnings/errors found (if any)
boolean issueFound = printDBIssues(connection);
// If issues found, exit with an error status (even if connection succeeded).
if (issueFound) {
System.exit(1);
} else {
System.exit(0);
}
} catch (SQLException sqle) {
System.err.println("\nError running 'test': ");
System.err.println(" - " + sqle);
@@ -170,16 +160,6 @@ public class DatabaseUtils {
"migrate'.");
}
}
// Print any database warnings/errors found (if any)
boolean issueFound = printDBIssues(connection);
// If issues found, exit with an error status
if (issueFound) {
System.exit(1);
} else {
System.exit(0);
}
} catch (SQLException e) {
System.err.println("Info exception:");
e.printStackTrace(System.err);
@@ -335,31 +315,6 @@ public class DatabaseUtils {
}
try (Connection connection = dataSource.getConnection()) {
String dbType = getDbType(connection);
// Not all Postgres user accounts will be able to run a 'clean',
// as only 'superuser' accounts can remove the 'pgcrypto' extension.
if (dbType.equals(DBMS_POSTGRES)) {
// Check if database user has permissions suitable to run a clean
if (!PostgresUtils.checkCleanPermissions(connection)) {
String username = connection.getMetaData().getUserName();
// Exit immediately, providing a descriptive error message
System.out.println(
"\nERROR: The database user '" + username + "' does not have sufficient " +
"privileges to run a 'database clean' (via Flyway).");
System.out.println(
"\nIn order to run a 'clean', the database user MUST have 'superuser' privileges");
System.out.println(
"OR the '" + PostgresUtils.PGCRYPTO + "' extension must be installed in a " +
"separate schema (see documentation).");
System.out.println(
"\nOptionally, you could also manually remove the '" + PostgresUtils.PGCRYPTO +
"' extension first (DROP EXTENSION " + PostgresUtils.PGCRYPTO +
" CASCADE;), then rerun the 'clean'");
System.exit(1);
}
}
BufferedReader input = new BufferedReader(new InputStreamReader(System.in,
StandardCharsets.UTF_8));
@@ -368,11 +323,6 @@ public class DatabaseUtils {
.println("\nWARNING: ALL DATA AND TABLES IN YOUR DATABASE WILL BE PERMANENTLY DELETED.\n");
System.out.println("There is NO turning back from this action. Backup your DB before " +
"continuing.");
if (dbType.equals(DBMS_POSTGRES)) {
System.out.println(
"\nPOSTGRES WARNING: the '" + PostgresUtils.PGCRYPTO + "' extension will be dropped " +
"if it is in the same schema as the DSpace database.\n");
}
System.out.print("Do you want to PERMANENTLY DELETE everything from your database? [y/n]: ");
String choiceString = input.readLine();
input.close();
@@ -476,108 +426,10 @@ public class DatabaseUtils {
"Database Software: " + meta.getDatabaseProductName() + " version " + meta.getDatabaseProductVersion());
System.out.println("Database Driver: " + meta.getDriverName() + " version " + meta.getDriverVersion());
// For Postgres, report whether pgcrypto is installed
// (If it isn't, we'll also write out warnings...see below)
if (dbType.equals(DBMS_POSTGRES)) {
boolean pgcryptoUpToDate = PostgresUtils.isPgcryptoUpToDate();
Double pgcryptoVersion = PostgresUtils.getPgcryptoInstalledVersion(connection);
System.out.println(
"PostgreSQL '" + PostgresUtils.PGCRYPTO + "' extension installed/up-to-date? " + pgcryptoUpToDate + "" +
" " + ((pgcryptoVersion != null) ? "(version=" + pgcryptoVersion + ")" : "(not installed)"));
}
// Finally, print out our version of Flyway
System.out.println("FlywayDB Version: " + VersionPrinter.getVersion());
}
/**
* Print any warnings about current database setup to System.err (if any).
* This is utilized by both the 'test' and 'info' commandline options.
*
* @param connection current database connection
* @return boolean true if database issues found, false otherwise
* @throws SQLException if database error occurs
*/
private static boolean printDBIssues(Connection connection) throws SQLException {
boolean issueFound = false;
// Get the DB Type
String dbType = getDbType(connection);
// For PostgreSQL databases, we need to check for the 'pgcrypto' extension.
// If it is NOT properly installed, we'll need to warn the user, as DSpace will be unable to proceed.
if (dbType.equals(DBMS_POSTGRES)) {
// Get version of pgcrypto available in this postgres instance
Double pgcryptoAvailable = PostgresUtils.getPgcryptoAvailableVersion(connection);
// Generic requirements message
String requirementsMsg = "\n** DSpace REQUIRES PostgreSQL >= " + PostgresUtils.POSTGRES_VERSION + " AND "
+ PostgresUtils.PGCRYPTO + " extension >= " + PostgresUtils.PGCRYPTO_VERSION + " **\n";
// Check if installed in PostgreSQL & a supported version
if (pgcryptoAvailable != null && pgcryptoAvailable.compareTo(PostgresUtils.PGCRYPTO_VERSION) >= 0) {
// We now know it's available in this Postgres. Let's see if it is installed in this database.
Double pgcryptoInstalled = PostgresUtils.getPgcryptoInstalledVersion(connection);
// Check if installed in database, but outdated version
if (pgcryptoInstalled != null && pgcryptoInstalled.compareTo(PostgresUtils.PGCRYPTO_VERSION) < 0) {
System.out.println(
"\nWARNING: Required PostgreSQL '" + PostgresUtils.PGCRYPTO + "' extension is OUTDATED " +
"(installed version=" + pgcryptoInstalled + ", available version = " + pgcryptoAvailable
+ ").");
System.out.println(requirementsMsg);
System.out.println(
"To update it, please connect to your DSpace database as a 'superuser' and manually run the " +
"following command: ");
System.out.println(
"\n ALTER EXTENSION " + PostgresUtils.PGCRYPTO + " UPDATE TO '" + pgcryptoAvailable + "';\n");
issueFound = true;
} else if (pgcryptoInstalled == null) {
// If it's not installed in database
System.out.println(
"\nWARNING: Required PostgreSQL '" + PostgresUtils.PGCRYPTO + "' extension is NOT INSTALLED " +
"on this database.");
System.out.println(requirementsMsg);
System.out.println(
"To install it, please connect to your DSpace database as a 'superuser' and manually run the " +
"following command: ");
System.out.println("\n CREATE EXTENSION " + PostgresUtils.PGCRYPTO + ";\n");
issueFound = true;
}
} else if (pgcryptoAvailable != null && pgcryptoAvailable.compareTo(PostgresUtils.PGCRYPTO_VERSION) < 0) {
// If installed in Postgres, but an unsupported version
System.out.println(
"\nWARNING: UNSUPPORTED version of PostgreSQL '" + PostgresUtils.PGCRYPTO + "' extension found " +
"(version=" + pgcryptoAvailable + ").");
System.out.println(requirementsMsg);
System.out.println(
"Make sure you are running a supported version of PostgreSQL, and then install " + PostgresUtils
.PGCRYPTO + " version >= " + PostgresUtils.PGCRYPTO_VERSION);
System.out.println(
"The '" + PostgresUtils.PGCRYPTO + "' extension is often provided in the 'postgresql-contrib' " +
"package for your operating system.");
issueFound = true;
} else if (pgcryptoAvailable == null) {
// If it's not installed in Postgres
System.out.println(
"\nWARNING: PostgreSQL '" + PostgresUtils.PGCRYPTO + "' extension is NOT AVAILABLE. Please " +
"install it into this PostgreSQL instance.");
System.out.println(requirementsMsg);
System.out.println(
"The '" + PostgresUtils.PGCRYPTO + "' extension is often provided in the 'postgresql-contrib' " +
"package for your operating system.");
System.out.println(
"Once the extension is installed globally, please connect to your DSpace database as a " +
"'superuser' and manually run the following command: ");
System.out.println("\n CREATE EXTENSION " + PostgresUtils.PGCRYPTO + ";\n");
issueFound = true;
}
}
return issueFound;
}
/**
* Setup/Initialize the Flyway Configuration to run against our DSpace database
* and point at our migration scripts.

View File

@@ -1,149 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.storage.rdbms;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.logging.log4j.Logger;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
/**
* This is a FlywayCallback class which automatically verifies that "pgcrypto"
* is at the proper version before running any database migrations.
* <P>
* When running PostgreSQL, pgcrypto is REQUIRED by DSpace as it allows UUIDs
* to be generated.
* <P>
* During a database "clean", this also de-registers "pgcrypto" proir to the
* full database clean.
*
* @author Tim Donohue
*/
public class PostgreSQLCryptoChecker implements Callback {
private Logger log = org.apache.logging.log4j.LogManager.getLogger(PostgreSQLCryptoChecker.class);
/**
* Check for pgcrypto (if needed). Throws an exception if pgcrypto is
* not installed or needs an upgrade.
*
* @param connection database connection
*/
public void checkPgCrypto(Connection connection) {
String dbType;
try {
dbType = DatabaseUtils.getDbType(connection);
} catch (SQLException se) {
throw new FlywayException("Unable to determine database type.", se);
}
// ONLY Check if this is a PostgreSQL database
if (dbType != null && dbType.equals(DatabaseUtils.DBMS_POSTGRES)) {
// If this is a PostgreSQL database, then a supported version
// of the 'pgcrypto' extension MUST be installed to continue.
// Check if pgcrypto is both installed & a supported version
if (!PostgresUtils.isPgcryptoUpToDate()) {
throw new FlywayException(
"This PostgreSQL Database is INCOMPATIBLE with DSpace. The upgrade will NOT proceed. " +
"A supported version (>=" + PostgresUtils.PGCRYPTO_VERSION + ") of the '" + PostgresUtils
.PGCRYPTO + "' extension must be installed! " +
"Please run 'dspace database info' for additional info/tips.");
}
}
}
/**
* Remove pgcrypto (if necessary).
* <P>
* The pgcrypto extension MUST be removed before a clean or else errors occur.
* This method checks if it needs to be removed and, if so, removes it.
*
* @param connection database connection
*/
public void removePgCrypto(Connection connection) {
try {
String dbType = DatabaseUtils.getDbType(connection);
// ONLY remove if this is a PostgreSQL database
if (dbType != null && dbType.equals(DatabaseUtils.DBMS_POSTGRES)) {
// Get current schema
String schema = DatabaseUtils.getSchemaName(connection);
// Check if pgcrypto is in this schema
// If so, it MUST be removed before a 'clean'
if (PostgresUtils.isPgcryptoInSchema(schema)) {
// remove the extension
try (Statement statement = connection.createStatement()) {
// WARNING: ONLY superusers can remove pgcrypto. However, at this point,
// we have already verified user acct permissions via PostgresUtils.checkCleanPermissions()
// (which is called prior to a 'clean' being triggered).
statement.execute("DROP EXTENSION " + PostgresUtils.PGCRYPTO + " CASCADE");
}
}
}
} catch (SQLException e) {
throw new FlywayException("Failed to check for and/or remove '" + PostgresUtils.PGCRYPTO + "' extension",
e);
}
}
/**
* The callback name, Flyway will use this to sort the callbacks alphabetically before executing them
* @return The callback name
*/
@Override
public String getCallbackName() {
// Return class name only (not prepended by package)
return PostgreSQLCryptoChecker.class.getSimpleName();
}
/**
* Events supported by this callback.
* @param event Flyway event
* @param context Flyway context
* @return true if BEFORE_BASELINE, BEFORE_MIGRATE or BEFORE_CLEAN
*/
@Override
public boolean supports(Event event, Context context) {
return event.equals(Event.BEFORE_BASELINE) || event.equals(Event.BEFORE_MIGRATE) ||
event.equals(Event.BEFORE_CLEAN);
}
/**
* Whether event can be handled in a transaction or whether it must be handle outside of transaction.
* @param event Flyway event
* @param context Flyway context
* @return true
*/
@Override
public boolean canHandleInTransaction(Event event, Context context) {
return true;
}
/**
* What to run when the callback is triggered.
* @param event Flyway event
* @param context Flyway context
*/
@Override
public void handle(Event event, Context context) {
// If, before initializing or migrating database, check for pgcrypto
// Else, before Cleaning database, remove pgcrypto (if exists)
if (event.equals(Event.BEFORE_BASELINE) || event.equals(Event.BEFORE_MIGRATE)) {
checkPgCrypto(context.getConnection());
} else if (event.equals(Event.BEFORE_CLEAN)) {
removePgCrypto(context.getConnection());
}
}
}

View File

@@ -1,218 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.storage.rdbms;
import static org.dspace.storage.rdbms.DatabaseUtils.getSchemaName;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.flywaydb.core.api.FlywayException;
/**
* Database utility class specific to Postgres.
* This class contains tools and methods which are useful in determining
* the status of a PostgreSQL database backend. It's a companion class
* to DatabaseUtils, but PostgreSQL specific.
*
* @author Tim Donohue
*/
public class PostgresUtils {
// PostgreSQL pgcrypto extension name, and required versions of Postgres & pgcrypto
public static final String PGCRYPTO = "pgcrypto";
public static final Double PGCRYPTO_VERSION = 1.1;
public static final Double POSTGRES_VERSION = 9.4;
/**
* Default constructor
*/
private PostgresUtils() { }
/**
* Get version of pgcrypto extension available. The extension is "available"
* if it's been installed via operating system tools/packages. It also
* MUST be installed in the DSpace database (see getPgcryptoInstalled()).
* <P>
* The pgcrypto extension is required for Postgres databases
*
* @param connection database connection
* @return version number or null if not available
*/
protected static Double getPgcryptoAvailableVersion(Connection connection) {
Double version = null;
String checkPgCryptoAvailable = "SELECT default_version AS version FROM pg_available_extensions WHERE name=?";
// Run the query to obtain the version of 'pgcrypto' available
try (PreparedStatement statement = connection.prepareStatement(checkPgCryptoAvailable)) {
statement.setString(1, PGCRYPTO);
try (ResultSet results = statement.executeQuery()) {
if (results.next()) {
version = results.getDouble("version");
}
}
} catch (SQLException e) {
throw new FlywayException("Unable to determine whether 'pgcrypto' extension is available.", e);
}
return version;
}
/**
* Get version of pgcrypto extension installed in the DSpace database.
* <P>
* The pgcrypto extension is required for Postgres databases to support
* UUIDs.
*
* @param connection database connection
* @return version number or null if not installed
*/
protected static Double getPgcryptoInstalledVersion(Connection connection) {
Double version = null;
String checkPgCryptoInstalled = "SELECT extversion AS version FROM pg_extension WHERE extname=?";
// Run the query to obtain the version of 'pgcrypto' installed on this database
try (PreparedStatement statement = connection.prepareStatement(checkPgCryptoInstalled)) {
statement.setString(1, PGCRYPTO);
try (ResultSet results = statement.executeQuery()) {
if (results.next()) {
version = results.getDouble("version");
}
}
} catch (SQLException e) {
throw new FlywayException("Unable to determine whether 'pgcrypto' extension is installed.", e);
}
return version;
}
/**
* Check if the pgcrypto extension is BOTH installed AND up-to-date.
* <P>
* This requirement is only needed for PostgreSQL databases.
* It doesn't matter what schema pgcrypto is installed in, as long as it exists.
*
* @return true if everything is installed and up-to-date. False otherwise.
*/
public static boolean isPgcryptoUpToDate() {
// Get our configured dataSource
DataSource dataSource = DatabaseUtils.getDataSource();
try (Connection connection = dataSource.getConnection()) {
Double pgcryptoInstalled = getPgcryptoInstalledVersion(connection);
// Check if installed & up-to-date in this DSpace database
if (pgcryptoInstalled != null && pgcryptoInstalled.compareTo(PGCRYPTO_VERSION) >= 0) {
return true;
}
return false;
} catch (SQLException e) {
throw new FlywayException("Unable to determine whether 'pgcrypto' extension is up-to-date.", e);
}
}
/**
* Check if the pgcrypto extension is installed into a particular schema
* <P>
* This allows us to check if pgcrypto needs to be REMOVED prior to running
* a 'clean' on this database. If pgcrypto is in the same schema as the
* dspace database, a 'clean' will require removing pgcrypto FIRST.
*
* @param schema name of schema
* @return true if pgcrypto is in this schema. False otherwise.
*/
public static boolean isPgcryptoInSchema(String schema) {
// Get our configured dataSource
DataSource dataSource = DatabaseUtils.getDataSource();
try (Connection connection = dataSource.getConnection()) {
// Check if pgcrypto is installed in the current database schema.
String pgcryptoInstalledInSchema = "SELECT extversion FROM pg_extension,pg_namespace " +
"WHERE pg_extension.extnamespace=pg_namespace.oid " +
"AND extname=? " +
"AND nspname=?;";
Double pgcryptoVersion = null;
try (PreparedStatement statement = connection.prepareStatement(pgcryptoInstalledInSchema)) {
statement.setString(1, PGCRYPTO);
statement.setString(2, schema);
try (ResultSet results = statement.executeQuery()) {
if (results.next()) {
pgcryptoVersion = results.getDouble("extversion");
}
}
}
// If a pgcrypto version returns, it's installed in this schema
if (pgcryptoVersion != null) {
return true;
} else {
return false;
}
} catch (SQLException e) {
throw new FlywayException(
"Unable to determine whether 'pgcrypto' extension is installed in schema '" + schema + "'.", e);
}
}
/**
* Check if the current user has permissions to run a clean on existing
* database.
* <P>
* Mostly this just checks if you need to remove pgcrypto, and if so,
* whether you have permissions to do so.
*
* @param connection database connection
* @return true if permissions valid, false otherwise
*/
protected static boolean checkCleanPermissions(Connection connection) {
try {
// get username of our db user
String username = connection.getMetaData().getUserName();
// Check their permissions. Are they a 'superuser'?
String checkSuperuser = "SELECT rolsuper FROM pg_roles WHERE rolname=?;";
boolean superuser = false;
try (PreparedStatement statement = connection.prepareStatement(checkSuperuser)) {
statement.setString(1, username);
try (ResultSet results = statement.executeQuery()) {
if (results.next()) {
superuser = results.getBoolean("rolsuper");
}
}
} catch (SQLException e) {
throw new FlywayException("Unable to determine if user '" + username + "' is a superuser.", e);
}
// If user is a superuser, then "clean" can be run successfully
if (superuser) {
return true;
} else {
// Otherwise, we'll need to see which schema 'pgcrypto' is installed in
// Get current schema name
String schema = getSchemaName(connection);
// If pgcrypto is installed in this schema, then superuser privileges are needed to remove it
if (isPgcryptoInSchema(schema)) {
return false;
} else {
// otherwise, a 'clean' can be run by anyone
return true;
}
}
} catch (SQLException e) {
throw new FlywayException("Unable to determine if DB user has 'clean' privileges.", e);
}
}
}

View File

@@ -27,7 +27,6 @@
<!-- Register all our Flyway callback classes (which run before/after database migrations) -->
<bean class="org.dspace.storage.rdbms.RegistryUpdater"/>
<bean class="org.dspace.storage.rdbms.GroupServiceInitializer"/>
<bean class="org.dspace.storage.rdbms.PostgreSQLCryptoChecker"/>
<bean class="org.dspace.storage.rdbms.SiteServiceInitializer"/>
<bean class="org.dspace.storage.rdbms.EntityTypeServiceInitializer"/>