Skip to content

Commit 0acd4b5

Browse files
Merge pull request #31235 from nextcloud/techdebt/noid/extract-request-id
Extract request id handling to dedicated class so it can be injected without DB dependency
2 parents b6209d6 + 67452b9 commit 0acd4b5

22 files changed

Lines changed: 421 additions & 327 deletions

apps/dav/tests/unit/Connector/Sabre/FileTest.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@
3535
use OC\Files\Storage\Temporary;
3636
use OC\Files\Storage\Wrapper\PermissionsMask;
3737
use OC\Files\View;
38-
use OC\Security\SecureRandom;
3938
use OCA\DAV\Connector\Sabre\File;
4039
use OCP\Constants;
4140
use OCP\Files\ForbiddenException;
4241
use OCP\Files\Storage;
4342
use OCP\IConfig;
43+
use OCP\IRequestId;
4444
use OCP\Lock\ILockingProvider;
45-
use OCP\Security\ISecureRandom;
45+
use PHPUnit\Framework\MockObject\MockObject;
4646
use Test\HookHelper;
4747
use Test\TestCase;
4848
use Test\Traits\MountProviderTrait;
@@ -64,11 +64,11 @@ class FileTest extends TestCase {
6464
*/
6565
private $user;
6666

67-
/** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */
67+
/** @var IConfig|MockObject */
6868
protected $config;
6969

70-
/** @var ISecureRandom */
71-
protected $secureRandom;
70+
/** @var IRequestId|MockObject */
71+
protected $requestId;
7272

7373
protected function setUp(): void {
7474
parent::setUp();
@@ -83,8 +83,8 @@ protected function setUp(): void {
8383

8484
$this->loginAsUser($this->user);
8585

86-
$this->config = $this->getMockBuilder('\OCP\IConfig')->getMock();
87-
$this->secureRandom = new SecureRandom();
86+
$this->config = $this->createMock(IConfig::class);
87+
$this->requestId = $this->createMock(IRequestId::class);
8888
}
8989

9090
protected function tearDown(): void {
@@ -96,7 +96,7 @@ protected function tearDown(): void {
9696
}
9797

9898
/**
99-
* @return \PHPUnit\Framework\MockObject\MockObject|Storage
99+
* @return MockObject|Storage
100100
*/
101101
private function getMockStorage() {
102102
$storage = $this->getMockBuilder(Storage::class)
@@ -184,7 +184,7 @@ public function testSimplePutFails($thrownException, $expectedException, $checkP
184184
->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]])
185185
->getMock();
186186
\OC\Files\Filesystem::mount($storage, [], $this->user . '/');
187-
/** @var View | \PHPUnit\Framework\MockObject\MockObject $view */
187+
/** @var View | MockObject $view */
188188
$view = $this->getMockBuilder(View::class)
189189
->setMethods(['getRelativePath', 'resolvePath'])
190190
->getMock();
@@ -330,7 +330,7 @@ private function doPut($path, $viewRoot = null, Request $request = null) {
330330
null
331331
);
332332

333-
/** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit\Framework\MockObject\MockObject $file */
333+
/** @var \OCA\DAV\Connector\Sabre\File | MockObject $file */
334334
$file = $this->getMockBuilder(\OCA\DAV\Connector\Sabre\File::class)
335335
->setConstructorArgs([$view, $info, null, $request])
336336
->setMethods(['header'])
@@ -416,7 +416,7 @@ public function testPutSingleFileLegalMtime($requestMtime, $resultMtime) {
416416
'server' => [
417417
'HTTP_X_OC_MTIME' => $requestMtime,
418418
]
419-
], $this->secureRandom, $this->config, null);
419+
], $this->requestId, $this->config, null);
420420
$file = 'foo.txt';
421421

422422
if ($resultMtime === null) {
@@ -439,7 +439,7 @@ public function testChunkedPutLegalMtime($requestMtime, $resultMtime) {
439439
'server' => [
440440
'HTTP_X_OC_MTIME' => $requestMtime,
441441
]
442-
], $this->secureRandom, $this->config, null);
442+
], $this->requestId, $this->config, null);
443443

444444
$_SERVER['HTTP_OC_CHUNKED'] = true;
445445
$file = 'foo.txt';

lib/base.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ public static function initPaths() {
161161
'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
162162
],
163163
];
164-
$fakeRequest = new \OC\AppFramework\Http\Request($params, new \OC\Security\SecureRandom(), new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
164+
$fakeRequest = new \OC\AppFramework\Http\Request(
165+
$params,
166+
new \OC\AppFramework\Http\RequestId($_SERVER['UNIQUE_ID'] ?? '', new \OC\Security\SecureRandom()),
167+
new \OC\AllConfig(new \OC\SystemConfig(self::$config))
168+
);
165169
$scriptName = $fakeRequest->getScriptName();
166170
if (substr($scriptName, -1) == '/') {
167171
$scriptName .= 'index.php';

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@
412412
'OCP\\INavigationManager' => $baseDir . '/lib/public/INavigationManager.php',
413413
'OCP\\IPreview' => $baseDir . '/lib/public/IPreview.php',
414414
'OCP\\IRequest' => $baseDir . '/lib/public/IRequest.php',
415+
'OCP\\IRequestId' => $baseDir . '/lib/public/IRequestId.php',
415416
'OCP\\ISearch' => $baseDir . '/lib/public/ISearch.php',
416417
'OCP\\IServerContainer' => $baseDir . '/lib/public/IServerContainer.php',
417418
'OCP\\ISession' => $baseDir . '/lib/public/ISession.php',
@@ -635,6 +636,7 @@
635636
'OC\\AppFramework\\Http\\Dispatcher' => $baseDir . '/lib/private/AppFramework/Http/Dispatcher.php',
636637
'OC\\AppFramework\\Http\\Output' => $baseDir . '/lib/private/AppFramework/Http/Output.php',
637638
'OC\\AppFramework\\Http\\Request' => $baseDir . '/lib/private/AppFramework/Http/Request.php',
639+
'OC\\AppFramework\\Http\\RequestId' => $baseDir . '/lib/private/AppFramework/Http/RequestId.php',
638640
'OC\\AppFramework\\Logger' => $baseDir . '/lib/private/AppFramework/Logger.php',
639641
'OC\\AppFramework\\Middleware\\AdditionalScriptsMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php',
640642
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
441441
'OCP\\INavigationManager' => __DIR__ . '/../../..' . '/lib/public/INavigationManager.php',
442442
'OCP\\IPreview' => __DIR__ . '/../../..' . '/lib/public/IPreview.php',
443443
'OCP\\IRequest' => __DIR__ . '/../../..' . '/lib/public/IRequest.php',
444+
'OCP\\IRequestId' => __DIR__ . '/../../..' . '/lib/public/IRequestId.php',
444445
'OCP\\ISearch' => __DIR__ . '/../../..' . '/lib/public/ISearch.php',
445446
'OCP\\IServerContainer' => __DIR__ . '/../../..' . '/lib/public/IServerContainer.php',
446447
'OCP\\ISession' => __DIR__ . '/../../..' . '/lib/public/ISession.php',
@@ -664,6 +665,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
664665
'OC\\AppFramework\\Http\\Dispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Dispatcher.php',
665666
'OC\\AppFramework\\Http\\Output' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Output.php',
666667
'OC\\AppFramework\\Http\\Request' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Request.php',
668+
'OC\\AppFramework\\Http\\RequestId' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/RequestId.php',
667669
'OC\\AppFramework\\Logger' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Logger.php',
668670
'OC\\AppFramework\\Middleware\\AdditionalScriptsMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php',
669671
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',

lib/private/AppFramework/Http/Request.php

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
use OC\Security\TrustedDomainHelper;
4949
use OCP\IConfig;
5050
use OCP\IRequest;
51+
use OCP\IRequestId;
5152
use OCP\Security\ICrypto;
52-
use OCP\Security\ISecureRandom;
5353

5454
/**
5555
* Class for accessing variables in the request.
@@ -92,12 +92,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
9292
'method',
9393
'requesttoken',
9494
];
95-
/** @var ISecureRandom */
96-
protected $secureRandom;
95+
/** @var RequestId */
96+
protected $requestId;
9797
/** @var IConfig */
9898
protected $config;
99-
/** @var string */
100-
protected $requestId = '';
10199
/** @var ICrypto */
102100
protected $crypto;
103101
/** @var CsrfTokenManager|null */
@@ -117,20 +115,20 @@ class Request implements \ArrayAccess, \Countable, IRequest {
117115
* - array 'cookies' the $_COOKIE array
118116
* - string 'method' the request method (GET, POST etc)
119117
* - string|false 'requesttoken' the requesttoken or false when not available
120-
* @param ISecureRandom $secureRandom
118+
* @param IRequestId $requestId
121119
* @param IConfig $config
122120
* @param CsrfTokenManager|null $csrfTokenManager
123121
* @param string $stream
124122
* @see https://www.php.net/manual/en/reserved.variables.php
125123
*/
126124
public function __construct(array $vars,
127-
ISecureRandom $secureRandom,
125+
IRequestId $requestId,
128126
IConfig $config,
129127
CsrfTokenManager $csrfTokenManager = null,
130128
string $stream = 'php://input') {
131129
$this->inputStream = $stream;
132130
$this->items['params'] = [];
133-
$this->secureRandom = $secureRandom;
131+
$this->requestId = $requestId;
134132
$this->config = $config;
135133
$this->csrfTokenManager = $csrfTokenManager;
136134

@@ -571,16 +569,7 @@ public function passesLaxCookieCheck(): bool {
571569
* @return string
572570
*/
573571
public function getId(): string {
574-
if (isset($this->server['UNIQUE_ID'])) {
575-
return $this->server['UNIQUE_ID'];
576-
}
577-
578-
if (empty($this->requestId)) {
579-
$validChars = ISecureRandom::CHAR_ALPHANUMERIC;
580-
$this->requestId = $this->secureRandom->generate(20, $validChars);
581-
}
582-
583-
return $this->requestId;
572+
return $this->requestId->getId();
584573
}
585574

586575
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2022, Joas Schilling <coding@schilljs.com>
6+
*
7+
* @author Joas Schilling <coding@schilljs.com>
8+
*
9+
* @license AGPL-3.0
10+
*
11+
* This code is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License, version 3,
13+
* as published by the Free Software Foundation.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License, version 3,
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>
22+
*
23+
*/
24+
namespace OC\AppFramework\Http;
25+
26+
use OCP\IRequestId;
27+
use OCP\Security\ISecureRandom;
28+
29+
class RequestId implements IRequestId {
30+
protected ISecureRandom $secureRandom;
31+
protected string $requestId;
32+
33+
public function __construct(string $uniqueId,
34+
ISecureRandom $secureRandom) {
35+
$this->requestId = $uniqueId;
36+
$this->secureRandom = $secureRandom;
37+
}
38+
39+
/**
40+
* Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
41+
* If `mod_unique_id` is installed this value will be taken.
42+
* @return string
43+
*/
44+
public function getId(): string {
45+
if (empty($this->requestId)) {
46+
$validChars = ISecureRandom::CHAR_ALPHANUMERIC;
47+
$this->requestId = $this->secureRandom->generate(20, $validChars);
48+
}
49+
50+
return $this->requestId;
51+
}
52+
}

lib/private/DB/Connection.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
use OC\SystemConfig;
5454
use OCP\DB\QueryBuilder\IQueryBuilder;
5555
use OCP\ILogger;
56+
use OCP\IRequestId;
5657
use OCP\PreConditionNotMetException;
5758

5859
class Connection extends \Doctrine\DBAL\Connection {
@@ -281,11 +282,16 @@ public function executeStatement($sql, array $params = [], array $types = []): i
281282
}
282283

283284
protected function logQueryToFile(string $sql): void {
284-
$logFile = $this->systemConfig->getValue('query_log_file', '');
285+
$logFile = $this->systemConfig->getValue('query_log_file');
285286
if ($logFile !== '' && is_writable(dirname($logFile)) && (!file_exists($logFile) || is_writable($logFile))) {
287+
$prefix = '';
288+
if ($this->systemConfig->getValue('query_log_file_requestid') === 'yes') {
289+
$prefix .= \OC::$server->get(IRequestId::class)->getId() . "\t";
290+
}
291+
286292
file_put_contents(
287293
$this->systemConfig->getValue('query_log_file', ''),
288-
$sql . "\n",
294+
$prefix . $sql . "\n",
289295
FILE_APPEND
290296
);
291297
}

lib/private/Server.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
use OC\App\AppStore\Fetcher\CategoryFetcher;
6161
use OC\AppFramework\Bootstrap\Coordinator;
6262
use OC\AppFramework\Http\Request;
63+
use OC\AppFramework\Http\RequestId;
6364
use OC\AppFramework\Utility\TimeFactory;
6465
use OC\Authentication\Events\LoginFailed;
6566
use OC\Authentication\Listeners\LoginFailedListener;
@@ -205,6 +206,7 @@
205206
use OCP\INavigationManager;
206207
use OCP\IPreview;
207208
use OCP\IRequest;
209+
use OCP\IRequestId;
208210
use OCP\ISearch;
209211
use OCP\IServerContainer;
210212
use OCP\ISession;
@@ -1033,7 +1035,7 @@ public function __construct($webRoot, \OC\Config $config) {
10331035
: '',
10341036
'urlParams' => $urlParams,
10351037
],
1036-
$this->get(ISecureRandom::class),
1038+
$this->get(IRequestId::class),
10371039
$this->get(\OCP\IConfig::class),
10381040
$this->get(CsrfTokenManager::class),
10391041
$stream
@@ -1042,6 +1044,13 @@ public function __construct($webRoot, \OC\Config $config) {
10421044
/** @deprecated 19.0.0 */
10431045
$this->registerDeprecatedAlias('Request', \OCP\IRequest::class);
10441046

1047+
$this->registerService(IRequestId::class, function (ContainerInterface $c): IRequestId {
1048+
return new RequestId(
1049+
$_SERVER['UNIQUE_ID'] ?? '',
1050+
$this->get(ISecureRandom::class)
1051+
);
1052+
});
1053+
10451054
$this->registerService(IMailer::class, function (Server $c) {
10461055
return new Mailer(
10471056
$c->get(\OCP\IConfig::class),
@@ -1214,7 +1223,7 @@ public function __construct($webRoot, \OC\Config $config) {
12141223
$this->registerAlias(EventDispatcherInterface::class, \OC\EventDispatcher\SymfonyAdapter::class);
12151224

12161225
$this->registerService('CryptoWrapper', function (ContainerInterface $c) {
1217-
// FIXME: Instantiiated here due to cyclic dependency
1226+
// FIXME: Instantiated here due to cyclic dependency
12181227
$request = new Request(
12191228
[
12201229
'get' => $_GET,
@@ -1227,7 +1236,7 @@ public function __construct($webRoot, \OC\Config $config) {
12271236
? $_SERVER['REQUEST_METHOD']
12281237
: null,
12291238
],
1230-
$c->get(ISecureRandom::class),
1239+
$c->get(IRequestId::class),
12311240
$c->get(\OCP\IConfig::class)
12321241
);
12331242

lib/public/IRequestId.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2022, Joas Schilling <coding@schilljs.com>
6+
*
7+
* @author Joas Schilling <coding@schilljs.com>
8+
*
9+
* @license AGPL-3.0
10+
*
11+
* This code is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License, version 3,
13+
* as published by the Free Software Foundation.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License, version 3,
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>
22+
*
23+
*/
24+
25+
namespace OCP;
26+
27+
/**
28+
* @since 24.0.0
29+
*/
30+
interface IRequestId {
31+
/**
32+
* Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
33+
* If `mod_unique_id` is installed this value will be taken.
34+
*
35+
* @return string
36+
* @since 24.0.0
37+
*/
38+
public function getId(): string;
39+
}

0 commit comments

Comments
 (0)