diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 007b19692c56a..9bd43f14d103f 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -219,6 +219,8 @@ 'OCP\\DB\\IPreparedStatement' => $baseDir . '/lib/public/DB/IPreparedStatement.php', 'OCP\\DB\\IResult' => $baseDir . '/lib/public/DB/IResult.php', 'OCP\\DB\\ISchemaWrapper' => $baseDir . '/lib/public/DB/ISchemaWrapper.php', + 'OCP\\DB\\QueryBuilder\\Events\\BeforeQueryExecutedEvent' => $baseDir . '/lib/public/DB/QueryBuilder/Events/BeforeQueryExecutedEvent.php', + 'OCP\\DB\\QueryBuilder\\Events\\QueryExecutedEvent' => $baseDir . '/lib/public/DB/QueryBuilder/Events/QueryExecutedEvent.php', 'OCP\\DB\\QueryBuilder\\ICompositeExpression' => $baseDir . '/lib/public/DB/QueryBuilder/ICompositeExpression.php', 'OCP\\DB\\QueryBuilder\\IExpressionBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IExpressionBuilder.php', 'OCP\\DB\\QueryBuilder\\IFunctionBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IFunctionBuilder.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index f9164a13aa3f4..e56047a9818eb 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -252,6 +252,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\DB\\IPreparedStatement' => __DIR__ . '/../../..' . '/lib/public/DB/IPreparedStatement.php', 'OCP\\DB\\IResult' => __DIR__ . '/../../..' . '/lib/public/DB/IResult.php', 'OCP\\DB\\ISchemaWrapper' => __DIR__ . '/../../..' . '/lib/public/DB/ISchemaWrapper.php', + 'OCP\\DB\\QueryBuilder\\Events\\BeforeQueryExecutedEvent' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/Events/BeforeQueryExecutedEvent.php', + 'OCP\\DB\\QueryBuilder\\Events\\QueryExecutedEvent' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/Events/QueryExecutedEvent.php', 'OCP\\DB\\QueryBuilder\\ICompositeExpression' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/ICompositeExpression.php', 'OCP\\DB\\QueryBuilder\\IExpressionBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IExpressionBuilder.php', 'OCP\\DB\\QueryBuilder\\IFunctionBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IFunctionBuilder.php', diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 2f97b4a146c27..2c5db3ebca283 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -48,11 +48,14 @@ use OC\DB\ResultAdapter; use OC\SystemConfig; use OCP\DB\IResult; +use OCP\DB\QueryBuilder\Events\BeforeQueryExecutedEvent; +use OCP\DB\QueryBuilder\Events\QueryExecutedEvent; use OCP\DB\QueryBuilder\ICompositeExpression; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; +use OCP\EventDispatcher\IEventDispatcher; use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { @@ -76,6 +79,9 @@ class QueryBuilder implements IQueryBuilder { /** @var string */ protected $lastInsertedTable; + /** @var IEventDispatcher */ + private $dispatcher; + /** * Initializes a new QueryBuilder. * @@ -88,6 +94,7 @@ public function __construct(ConnectionAdapter $connection, SystemConfig $systemC $this->logger = $logger; $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner()); $this->helper = new QuoteHelper(); + $this->dispatcher = \OC::$server->get(IEventDispatcher::class); } /** @@ -277,7 +284,18 @@ public function execute() { ]); } - $result = $this->queryBuilder->execute(); + $event = new BeforeQueryExecutedEvent($this); + $this->dispatcher->dispatchTyped($event); + $result = $event->getResult(); + + if ($result === null) { + $result = $this->queryBuilder->execute(); + } + + $event = new QueryExecutedEvent($this, $result); + $this->dispatcher->dispatchTyped($event); + $result = $event->getResult(); + if (is_int($result)) { return $result; } diff --git a/lib/public/DB/QueryBuilder/Events/BeforeQueryExecutedEvent.php b/lib/public/DB/QueryBuilder/Events/BeforeQueryExecutedEvent.php new file mode 100644 index 0000000000000..a42e504e83a73 --- /dev/null +++ b/lib/public/DB/QueryBuilder/Events/BeforeQueryExecutedEvent.php @@ -0,0 +1,90 @@ +. + * + */ + +namespace OCP\DB\QueryBuilder\Events; + +use OCP\EventDispatcher\Event; +use OCP\DB\QueryBuilder\IQueryBuilder; +use Doctrine\DBAL\Result; + +/** + * This event is used by apps to intercept, inspect, and potentially modify + * database queries prior to execution. This can be used for deep integrations, + * restricting user access to certain data, redacting information, etc. + * + * The result field can optionally be set by the event listener by executing the + * query in the event handler logic. This allows apps to inspect the results, + * use try/catch blocks around the query execution, and/or modify/re-execute the + * query if necessary. If the result field is not set, the QueryBuilder class + * will execute the query as expected by default. + * + * @see https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/projects.html + * @since 28.0.0 + */ +class BeforeQueryExecutedEvent extends Event { + private IQueryBuilder $queryBuilder; + private $result = null; + + /** + * @param IQueryBuilder $queryBuilder + * @since 28.0.0 + */ + public function __construct(IQueryBuilder $queryBuilder) { + $this->queryBuilder = $queryBuilder; + } + + /** + * @return IQueryBuilder + * @since 28.0.0 + */ + public function getQueryBuilder(): IQueryBuilder { + return $this->queryBuilder; + } + + /** + * @param IQueryBuilder $queryBuilder + * @since 28.0.0 + */ + public function setQueryBuilder(IQueryBuilder $queryBuilder) { + $this->queryBuilder = $queryBuilder; + } + + /** + * @return Result|int|string + * @since 28.0.0 + */ + public function getResult() { + return $this->result; + } + + /** + * @param Result|int|string $result + * @since 28.0.0 + */ + public function setResult($result) { + $this->result = $result; + } +} diff --git a/lib/public/DB/QueryBuilder/Events/QueryExecutedEvent.php b/lib/public/DB/QueryBuilder/Events/QueryExecutedEvent.php new file mode 100644 index 0000000000000..f745c8b33743a --- /dev/null +++ b/lib/public/DB/QueryBuilder/Events/QueryExecutedEvent.php @@ -0,0 +1,87 @@ +. + * + */ + +namespace OCP\DB\QueryBuilder\Events; + +use OCP\EventDispatcher\Event; +use OCP\DB\QueryBuilder\IQueryBuilder; +use Doctrine\DBAL\Result; + +/** + * This event is used by apps to intercept, inspect, and potentially modify + * the results of database queries after execution. This can be used for deep + * integrations, restricting user access to certain data, redacting information, + * etc. + * + * @see https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/projects.html + * @since 28.0.0 + */ +class QueryExecutedEvent extends Event { + private IQueryBuilder $queryBuilder; + private $result; + + /** + * @param IQueryBuilder $queryBuilder + * @param $result + * @since 28.0.0 + */ + public function __construct(IQueryBuilder $queryBuilder, $result) { + $this->queryBuilder = $queryBuilder; + $this->result = $result; + } + + /** + * @return IQueryBuilder + * @since 28.0.0 + */ + public function getQueryBuilder(): IQueryBuilder { + return $this->queryBuilder; + } + + /** + * @param IQueryBuilder $queryBuilder + * @since 28.0.0 + */ + public function setQueryBuilder(IQueryBuilder $queryBuilder) { + $this->queryBuilder = $queryBuilder; + } + + /** + * @return Result|int|string + * @since 28.0.0 + */ + public function getResult() { + return $this->result; + } + + /** + * @param Result|int|string $result + * @since 28.0.0 + */ + public function setResult($result) { + $this->result = $result; + } +}