forked from GEWIS/gewisdb
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add DBAL middleware to ensure proper role is used for all queries
Initially, we attempted to use the `wrapperClass` option to handle setting the database role. However, we encountered issues because DBAL checks for the exact `Connection` class type and not the interface, making it difficult to extend the `Connection` class as needed. We also considered overwriting the PgSQL-specific connection class (PDO variant) to set the role upon connection. Unfortunately, this was not an option because the class is declared as `final`, preventing us from extending it. The next potential solution was to use an `EventSubscriber` to set the role after the connection was established (using `postConnect`). However, this approach is already deprecated in our version of DBAL and completely removed in the next major release, rendering it unsuitable for us (maintainability). Ultimately, we implemented the `SET ROLE` functionality using DBAL's middleware. By wrapping the driver, and manually creating the `Connection` we can perform the `SET ROLE` query before the connection is used by the application. Runtime checks exist to ensure that the role for each database is defined. However, validation of the actual value is done by PostgreSQL itself (it will complain if the role does not exist). Co-Authored-By: Rink <[email protected]>
- Loading branch information
Showing
6 changed files
with
142 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
module/Application/src/Extensions/Doctrine/Middleware/Driver.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Application\Extensions\Doctrine\Middleware; | ||
|
||
use Doctrine\DBAL\Driver as DriverInterface; | ||
use Doctrine\DBAL\Driver\Connection as ConnectionInterface; | ||
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; | ||
use SensitiveParameter; | ||
|
||
use function implode; | ||
|
||
class Driver extends AbstractDriverMiddleware | ||
{ | ||
/** | ||
* @param array<non-empty-string, non-empty-string> $roles | ||
*/ | ||
public function __construct( | ||
DriverInterface $driver, | ||
private readonly array $roles, | ||
private readonly bool $isPgSQL, | ||
) { | ||
parent::__construct($driver); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function connect( | ||
#[SensitiveParameter] | ||
array $params, | ||
): ConnectionInterface { | ||
$connection = parent::connect($params); | ||
|
||
if ( | ||
$this->isPgSQL | ||
&& isset($params['host'], $params['port'], $params['dbname']) | ||
) { | ||
$role = $this->roles[implode(':', [$params['host'], $params['port'], $params['dbname']])]; | ||
|
||
$connection->exec('SET ROLE ' . $connection->quote($role)); | ||
} | ||
|
||
return $connection; | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
module/Application/src/Extensions/Doctrine/Middleware/SetRoleMiddleware.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Application\Extensions\Doctrine\Middleware; | ||
|
||
use Doctrine\DBAL\Driver as DriverInterface; | ||
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; | ||
use RuntimeException; | ||
|
||
use function getenv; | ||
use function implode; | ||
|
||
class SetRoleMiddleware implements MiddlewareInterface | ||
{ | ||
public function wrap(DriverInterface $driver): DriverInterface | ||
{ | ||
$isPgSQL = $driver instanceof DriverInterface\PDO\PgSQL\Driver; | ||
if ( | ||
!$isPgSQL | ||
&& !$driver instanceof DriverInterface\PDO\SQLite\Driver | ||
) { | ||
throw new RuntimeException('Expected DBAL Driver to be PDO PgSQL/Sqlite, but got ' . $driver::class); | ||
} | ||
|
||
$roleDefaultHost = getenv('DOCTRINE_DEFAULT_HOST'); | ||
$roleDefaultPort = getenv('DOCTRINE_DEFAULT_PORT'); | ||
$roleDefaultDB = getenv('DOCTRINE_DEFAULT_DATABASE'); | ||
$roleDefaultRole = getenv('DOCTRINE_DEFAULT_ROLE'); | ||
|
||
$roleReportHost = getenv('DOCTRINE_REPORT_HOST'); | ||
$roleReportPort = getenv('DOCTRINE_REPORT_PORT'); | ||
$roleReportDB = getenv('DOCTRINE_REPORT_DATABASE'); | ||
$roleReportRole = getenv('DOCTRINE_REPORT_ROLE'); | ||
|
||
if ( | ||
false === $roleDefaultHost | ||
|| false === $roleDefaultPort | ||
|| false === $roleDefaultDB | ||
|| false === $roleDefaultRole | ||
) { | ||
throw new RuntimeException('Required `DOCTRINE_DEFAULT_*` environment variables not set...'); | ||
} | ||
|
||
if ( | ||
false === $roleReportHost | ||
|| false === $roleReportPort | ||
|| false === $roleReportDB | ||
|| false === $roleReportRole | ||
) { | ||
throw new RuntimeException('Required `DOCTRINE_REPORT_*` environment variables not set...'); | ||
} | ||
|
||
$roles = [ | ||
implode( | ||
':', | ||
[ | ||
$roleDefaultHost, | ||
$roleDefaultPort, | ||
$roleDefaultDB, | ||
], | ||
) => $roleDefaultRole, | ||
implode( | ||
':', | ||
[ | ||
$roleReportHost, | ||
$roleReportPort, | ||
$roleReportDB, | ||
], | ||
) => $roleReportRole, | ||
]; | ||
|
||
return new Driver($driver, $roles, $isPgSQL); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters