added SSL-related PostgreSQL configuration

Added a new SSL-related configuration options to the top-level
configuration level for PostgreSQL database. They are sslmode, sslcert,
sslkey, and sslrootcert.

These options are copied from the top-level configuration to the options
option hash during configuration parsing
by the PostgresConfigurationParser.

If both are defined, then the top-level option overwrites a option
in the 'options' hash.

 - also added constants
 - functional test
 - updated docs
This commit is contained in:
silverqx
2023-02-01 10:53:54 +01:00
parent 5ec7b0a99c
commit 9910668a8a
8 changed files with 242 additions and 7 deletions

View File

@@ -206,7 +206,99 @@ You have to pass the [`sslmode`](https://www.postgresql.org/docs/current/libpq-s
And place your __client__ certificates to the `~/.postgres/` on [Linux](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-FILE-USAGE) and `$env:APPDATA/postgres/` on Windows. Everything is described in the PostgreSQL's [libpq client](https://www.postgresql.org/docs/current/libpq-ssl.html) and [server](https://www.postgresql.org/docs/15/ssl-tcp.html#SSL-FILE-USAGE) documentation.
You may also use the `ConfigUtils::postgresSslOptions()` or the `ConfigUtils::insertPostgresSslOptions()` methods to insert the `sslmode` option for you and define it using the `DB_PGSQL_SSLMODE` environment variable.
If you want to keep your __client__ certificates in your own location, you can set the `sslcert`, `sslkey`, and `sslrootcert` options.
<Tabs groupId={shell}>
<TabItem value={pwsh} label={pwsh_label}>
#include <orm/db.hpp>
using Orm::DB;
// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QPSQL"},
{"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},
...
// highlight-next-line
{"options", QVariantHash({{"sslmode", "verify-full"},
// highlight-start
{"sslcert", "C:/example/postgres.crt"},
{"sslkey", "C:/example/postgres.key"},
{"sslrootcert", "C:/example/root.crt"}})},
// highlight-end
});
</TabItem>
<TabItem value={bash} label={bash_label}>
#include <orm/db.hpp>
using Orm::DB;
// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QPSQL"},
{"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},
...
// highlight-next-line
{"options", QVariantHash({{"sslmode", "verify-full"},
// highlight-start
{"sslcert", "/example/postgres.crt"},
{"sslkey", "/example/postgres.key"},
{"sslrootcert", "/example/root.crt"}})},
// highlight-end
});
</TabItem>
</Tabs>
You can define these SSL-related options in the top-level configuration, they will be copied to the `options` option hash during configuration parsing. The top-level configuration takes precedence and overwrites the options in the `options` hash.
<Tabs groupId={shell}>
<TabItem value={pwsh} label={pwsh_label}>
#include <orm/db.hpp>
using Orm::DB;
// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QPSQL"},
{"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},
...
// highlight-start
{"sslmode", "verify-full"},
{"sslcert", "C:/example/postgres.crt"},
{"sslkey", "C:/example/postgres.key"},
{"sslrootcert", "C:/example/root.crt"}
// highlight-end
});
</TabItem>
<TabItem value={bash} label={bash_label}>
#include <orm/db.hpp>
using Orm::DB;
// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QPSQL"},
{"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},
...
// highlight-start
{"sslmode", "verify-full"},
{"sslcert", "/example/postgres.crt"},
{"sslkey", "/example/postgres.key"},
{"sslrootcert", "/example/root.crt"}
// highlight-end
});
</TabItem>
</Tabs>
You may also use the `ConfigUtils::postgresSslOptions()` or the `ConfigUtils::insertPostgresSslOptions()` methods to insert the `sslmode`, `sslcert`, `sslkey`, and `sslrootcert` options for you and define them using the `DB_PGSQL_SSLMODE`, `DB_PGSQL_SSLCERT`, `DB_PGSQL_SSLKEY`, and `DB_PGSQL_SSLROOTCERT` environment variable.
#include <orm/db.hpp>
#include <orm/utils/configuration.hpp>
@@ -225,7 +317,7 @@ You may also use the `ConfigUtils::postgresSslOptions()` or the `ConfigUtils::in
});
:::info
The PostgreSQL's libpq client library provides the [`PGSSLMODE`](https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.13.1.1) environment variable, so you don't have to use TinyORM's `options` configuration and may use the `PGSSLMODE` instead.
The PostgreSQL's libpq client library provides the [`PGSSLMODE`](https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.13.1.1), [`PGSSLCERT`](https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.16.1.1), [`PGSSLKEY`](https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.17.1.1), and [`PGSSLROOTCERT`](https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.18.1.1) environment variables, so you don't have to use TinyORM's `options` configuration and may use these environment variables instead.
:::
:::tip

View File

@@ -29,6 +29,10 @@ namespace Orm::Configurations
void parseDriverSpecificOptions() const final;
/*! Parse the driver-specific 'options' configuration option. */
void parseDriverSpecificOptionsOption(QVariantHash &options) const final;
private:
/*! Add the SSL-related options to the connection 'options' hash. */
void addSslOptions(QVariantHash &options) const;
};
} // namespace Orm::Configurations

View File

@@ -99,6 +99,9 @@ namespace Orm::Constants
// PostgreSQL SSL
SHAREDLIB_EXPORT extern const QString sslmode_;
SHAREDLIB_EXPORT extern const QString sslcert;
SHAREDLIB_EXPORT extern const QString sslkey;
SHAREDLIB_EXPORT extern const QString sslrootcert;
SHAREDLIB_EXPORT extern const QString isolation_level;
SHAREDLIB_EXPORT extern const QString foreign_key_constraints;

View File

@@ -98,6 +98,9 @@ namespace Orm::Constants
// PostgreSQL SSL
inline const QString sslmode_ = QStringLiteral("sslmode");
inline const QString sslcert = QStringLiteral("sslcert");
inline const QString sslkey = QStringLiteral("sslkey");
inline const QString sslrootcert = QStringLiteral("sslrootcert");
inline const QString
isolation_level = QStringLiteral("isolation_level");

View File

@@ -6,6 +6,10 @@ TINYORM_BEGIN_COMMON_NAMESPACE
using Orm::Constants::dont_drop;
using Orm::Constants::spatial_ref_sys;
using Orm::Constants::sslmode_;
using Orm::Constants::sslcert;
using Orm::Constants::sslkey;
using Orm::Constants::sslrootcert;
namespace Orm::Configurations
{
@@ -20,8 +24,39 @@ void PostgresConfigurationParser::parseDriverSpecificOptions() const
}
void PostgresConfigurationParser::parseDriverSpecificOptionsOption(
QVariantHash &/*unused*/) const
{}
QVariantHash &options) const
{
// Copy all SSL-related options from the top-level config. to the 'options' hash
addSslOptions(options);
}
/* private */
void PostgresConfigurationParser::addSslOptions(QVariantHash &options) const
{
/* Copy all SSL-related connection options from the top-level configuration level
to the 'options' hash. If the options hash already contains the same option, then
it will be overwritten. */
for (auto &&option : {sslmode_, sslcert, sslkey, sslrootcert}) {
// Nothing to do, the original configuration doesn't contain it
if (!config().contains(option))
continue;
// Copy the value to the 'options' hash
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (const auto &newValue = config()[option];
!newValue.value<QString>().isEmpty()
)
options.insert(option, newValue);
#else
if (auto newValue = config().value(option);
!newValue.value<QString>().isEmpty()
)
options.emplace(std::move(const_cast<QString &>(option)),
std::move(newValue));
#endif
}
}
} // namespace Orm::Configurations

View File

@@ -88,6 +88,9 @@ namespace Orm::Constants
// PostgreSQL SSL
const QString sslmode_ = QStringLiteral("sslmode");
const QString sslcert = QStringLiteral("sslcert");
const QString sslkey = QStringLiteral("sslkey");
const QString sslrootcert = QStringLiteral("sslrootcert");
const QString isolation_level = QStringLiteral("isolation_level");
const QString foreign_key_constraints = QStringLiteral("foreign_key_constraints");

View File

@@ -159,9 +159,13 @@ namespace
QVariantHash getPostgresSslOptions()
{
QVariantHash options;
options.reserve(1);
options.reserve(4);
insertDbOption(options, sslmode_, qEnvironmentVariable("DB_PGSQL_SSLMODE"));
insertDbOption(options, sslmode_, qEnvironmentVariable("DB_PGSQL_SSLMODE"));
insertDbOption(options, sslcert, qEnvironmentVariable("DB_PGSQL_SSLCERT"));
insertDbOption(options, sslkey, qEnvironmentVariable("DB_PGSQL_SSLKEY"));
insertDbOption(options, sslrootcert,
qEnvironmentVariable("DB_PGSQL_SSLROOTCERT"));
return options;
}
@@ -170,7 +174,7 @@ namespace
QVariantHash Configuration::postgresSslOptions()
{
QVariantHash options;
options.reserve(6);
options.reserve(9);
options.insert(getPostgresSslOptions());

View File

@@ -31,6 +31,10 @@ using Orm::Constants::qt_timezone;
using Orm::Constants::return_qdatetime;
using Orm::Constants::schema_;
using Orm::Constants::spatial_ref_sys;
using Orm::Constants::sslcert;
using Orm::Constants::sslkey;
using Orm::Constants::sslmode_;
using Orm::Constants::sslrootcert;
using Orm::Constants::username_;
using Orm::DatabaseManager;
@@ -52,6 +56,8 @@ private Q_SLOTS:
void default_PostgreSQL_ConfigurationValues() const;
void default_SQLite_ConfigurationValues() const;
void ssl_PostgreSQL_ConfigurationValues() const;
// NOLINTNEXTLINE(readability-redundant-access-specifiers)
private:
/*! The Database Manager used in this test case. */
@@ -322,6 +328,91 @@ void tst_DatabaseManager::default_SQLite_ConfigurationValues() const
// Restore
QVERIFY(m_dm->removeConnection(connectionName));
}
void tst_DatabaseManager::ssl_PostgreSQL_ConfigurationValues() const
{
const auto connectionName =
QStringLiteral(
"tinyorm_pgsql_tests-tst_DatabaseMannager-"
"ssl_PostgreSQL_ConfigurationValues");
// Top level
const auto sslmodeValue = QStringLiteral("verify-full");
const auto sslcertValue = QStringLiteral("C:/example/psql.crt");
const auto sslkeyValue = QStringLiteral("C:/example/psql.key");
const auto sslrootcertValue = QStringLiteral("C:/example/ca.crt");
// The 'options' level
const auto sslcertOptionsValue = QStringLiteral("D:/example/pg.crt");
const auto sslkeyOptionsValue = QStringLiteral("D:/example/pg.key");
const QVariantHash initialConfiguration({
{driver_, QPSQL},
{sslmode_, sslmodeValue},
{sslcert, sslcertValue},
{sslkey, sslkeyValue},
{sslrootcert, sslrootcertValue},
{options_, QVariantHash({{sslcert, sslcertOptionsValue},
{sslkey, sslkeyOptionsValue}})}
});
// Create database connection
m_dm->addConnections({
{connectionName, initialConfiguration},
// Don't setup any default connection
}, EMPTY);
// Original configuration
// Connection isn't created and configuration options are not parsed yet
const auto &originalConfig = m_dm->originalConfig(connectionName);
QCOMPARE(originalConfig, initialConfiguration);
/* Force the creation of a connection and parse the connection configuration options.
The SSL-related options are only parsed in the connection configuration,
the original configuration is untouched. */
m_dm->connection(connectionName);
QCOMPARE(originalConfig,
QVariantHash({
{driver_, QPSQL},
{NAME, connectionName},
{database_, EMPTY},
{prefix_, EMPTY},
{prefix_indexes, false},
{options_, QVariantHash()},
{dont_drop, QStringList {spatial_ref_sys}},
{sslmode_, sslmodeValue},
{sslcert, sslcertValue},
{sslkey, sslkeyValue},
{sslrootcert, sslrootcertValue},
{options_, QVariantHash({{sslcert, sslcertOptionsValue},
{sslkey, sslkeyOptionsValue}})}
}));
// Connection configuration
QCOMPARE(m_dm->getConfig(connectionName),
QVariantHash({
{driver_, QPSQL},
{NAME, connectionName},
{database_, EMPTY},
{prefix_, EMPTY},
{prefix_indexes, false},
{options_, QVariantHash()},
{dont_drop, QStringList {spatial_ref_sys}},
{qt_timezone, QVariant::fromValue(
QtTimeZoneConfig {QtTimeZoneType::DontConvert, {}}
)},
{sslmode_, sslmodeValue},
{sslcert, sslcertValue},
{sslkey, sslkeyValue},
{sslrootcert, sslrootcertValue},
{options_, QVariantHash({{sslmode_, sslmodeValue},
{sslcert, sslcertValue},
{sslkey, sslkeyValue},
{sslrootcert, sslrootcertValue}})}
}));
// Restore
QVERIFY(m_dm->removeConnection(connectionName));
}
// NOLINTEND(readability-convert-member-functions-to-static)
QTEST_MAIN(tst_DatabaseManager)