Skip to content

Commit 6cbea66

Browse files
authored
Merge pull request #993 from nextcloud/port/fixes
Port/fixes
2 parents 61dfbd2 + f738b1c commit 6cbea66

3 files changed

Lines changed: 127 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616
### Fixed
1717

1818
- Fix: Handle projects whose parent project is unknown [#985](https://github.com/nextcloud/integration_openproject/pull/985)
19+
- Fix: Force HTTPS on Nextcloud base URL for OpenProject API requests [#992](https://github.com/nextcloud/integration_openproject/pull/992)
1920

2021
### Added
2122

2223
- [BREAKING] Support Nextcloud 33 [#952](https://github.com/nextcloud/integration_openproject/pull/952)
24+
- Include OpenProject API request as debug log [#991](https://github.com/nextcloud/integration_openproject/pull/991)
2325

2426
### Changed
2527

lib/Service/OpenProjectAPIService.php

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,25 @@ public function getNotifications(string $userId): array {
246246
}
247247

248248
/**
249-
* wrapper around IURLGenerator::getBaseUrl() to make it easier to mock in tests
249+
* @return string
250250
*/
251-
public function getBaseUrl(): string {
252-
return $this->config->getSystemValueString('overwrite.cli.url');
251+
public function getNCBaseUrl(): string {
252+
$message = 'Invalid or missing "overwrite.cli.url" system configuration.';
253+
$ncUrl = $this->config->getSystemValueString('overwrite.cli.url');
254+
if (!$ncUrl) {
255+
$this->logger->error($message, ['app' => $this->appName]);
256+
return '';
257+
}
258+
259+
$parsedUrl = parse_url($ncUrl);
260+
if (!$parsedUrl || !isset($parsedUrl['scheme']) || !isset($parsedUrl['host'])) {
261+
$this->logger->error($message, ['app' => $this->appName]);
262+
return '';
263+
}
264+
if ($parsedUrl['scheme'] !== 'https') {
265+
$ncUrl = str_replace('http://', 'https://', $ncUrl);
266+
}
267+
return $ncUrl;
253268
}
254269

255270
/**
@@ -303,7 +318,7 @@ private function searchRequest(string $userId, array $filters, bool $onlyLinkabl
303318
if ($onlyLinkableWorkPackages) {
304319
$filters[] = [
305320
'linkable_to_storage_url' =>
306-
['operator' => '=', 'values' => [urlencode($this->getBaseUrl())]]
321+
['operator' => '=', 'values' => [urlencode($this->getNCBaseUrl())]]
307322
];
308323
}
309324

@@ -439,6 +454,15 @@ public function rawRequest(
439454
}
440455
}
441456

457+
$sanitizedOptions = $this->sanitizeReqOptionsForLog($options);
458+
$requestLog = json_encode([
459+
'method' => $method,
460+
'url' => $url,
461+
'headers' => $sanitizedOptions['headers'],
462+
'body' => $sanitizedOptions['body'],
463+
]);
464+
$this->logger->debug('OpenProject API request: ' . $requestLog, ['app' => $this->appName]);
465+
442466
if ($method === 'GET') {
443467
$response = $this->client->get($url, $options);
444468
} elseif ($method === 'POST') {
@@ -757,7 +781,7 @@ public function linkWorkPackageToFile(
757781
],
758782
'_links' => [
759783
'storageUrl' => [
760-
'href' => $this->getBaseUrl()
784+
'href' => $this->getNCBaseUrl()
761785
]
762786
]
763787
];
@@ -1511,7 +1535,7 @@ public function getAvailableOpenProjectProjects(string $userId, ?string $searchQ
15111535
}
15121536
$filters[] = [
15131537
'storageUrl' =>
1514-
['operator' => '=', 'values' => [$this->getBaseUrl()]],
1538+
['operator' => '=', 'values' => [$this->getNCBaseUrl()]],
15151539
'userAction' =>
15161540
['operator' => '&=', 'values' => ["file_links/manage", "work_packages/create"]]
15171541
];
@@ -1867,4 +1891,42 @@ public function getAppsName(string $appId): string {
18671891

18681892
return $appInfo['name'];
18691893
}
1894+
1895+
/**
1896+
* @param array $options
1897+
*
1898+
* @return array
1899+
*/
1900+
public function sanitizeReqOptionsForLog(array $options): array {
1901+
$sanitizedOptions = [
1902+
'headers' => [],
1903+
'body' => null,
1904+
];
1905+
$headers = ['authorization', 'cookie', 'set-cookie'];
1906+
if (isset($options['headers'])) {
1907+
foreach ($options['headers'] as $key => $value) {
1908+
if (in_array(strtolower($key), $headers)) {
1909+
$sanitizedOptions['headers'][$key] = '[REDACTED]';
1910+
} else {
1911+
$sanitizedOptions['headers'][$key] = $value;
1912+
}
1913+
}
1914+
}
1915+
1916+
if (isset($options['body'])) {
1917+
try {
1918+
$body = json_decode($options['body'], true, 512, JSON_THROW_ON_ERROR);
1919+
$fields = ['password', 'client_secret', 'refresh_token', 'access_token', 'token'];
1920+
foreach ($fields as $field) {
1921+
if (isset($body[$field])) {
1922+
$body[$field] = '[REDACTED]';
1923+
}
1924+
}
1925+
$sanitizedOptions['body'] = $body;
1926+
} catch (\JsonException) {
1927+
$sanitizedOptions['body'] = '[DATA]';
1928+
}
1929+
}
1930+
return $sanitizedOptions;
1931+
}
18701932
}

tests/lib/Service/OpenProjectAPIServiceTest.php

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -803,10 +803,6 @@ private function getOpenProjectAPIService(
803803
$appManagerMock->method('isInstalled')->willReturn(true);
804804
}
805805

806-
$urlGeneratorMock = $this->getMockBuilder(IURLGenerator::class)->getMock();
807-
$urlGeneratorMock
808-
->method('getBaseUrl')
809-
->willReturn($baseUrl);
810806
$this->defaultConfigMock
811807
->method('getSystemValueString')
812808
->with($this->equalTo('overwrite.cli.url'))
@@ -817,7 +813,6 @@ private function getOpenProjectAPIService(
817813
'config' => $this->defaultConfigMock,
818814
'clientService' => $clientService,
819815
'rootFolder' => $storageMock,
820-
'urlGenerator' => $urlGeneratorMock,
821816
'appManager' => $appManagerMock,
822817
'tokenEventFactory' => $exchangeTokenMock,
823818
]);
@@ -831,21 +826,27 @@ private function getOpenProjectAPIService(
831826
*
832827
* @param array<string> $mockMethods
833828
* @param array<string, object> $constructParams
829+
* @param bool $mockBaseUrl
834830
*
835831
* @return OpenProjectAPIService|MockObject
836832
*/
837833
private function getOpenProjectAPIServiceMock(
838834
array $mockMethods = ['request'],
839835
array $constructParams = [],
836+
bool $mockBaseUrl = true
840837
): OpenProjectAPIService|MockObject {
841-
$mockMethods[] = 'getBaseUrl';
838+
if ($mockBaseUrl) {
839+
$mockMethods[] = 'getNCBaseUrl';
840+
}
842841
$constructArgs = $this->getOpenProjectAPIServiceConstructArgs($constructParams);
843842

844843
$mock = $this->getMockBuilder(OpenProjectAPIService::class)
845844
->setConstructorArgs($constructArgs)
846845
->onlyMethods($mockMethods)
847846
->getMock();
848-
$mock->method('getBaseUrl')->willReturn('https://nc.my-server.org');
847+
if ($mockBaseUrl) {
848+
$mock->method('getNCBaseUrl')->willReturn('https://nc.my-server.org');
849+
}
849850
return $mock;
850851
}
851852

@@ -3906,14 +3907,7 @@ public function testGetAvailableOpenProjectProjectsErrorResponse(): void {
39063907
* @return void
39073908
*/
39083909
public function testGetAvailableOpenProjectProjectsQueryOnly() {
3909-
$iUrlGeneratorMock = $this->getMockBuilder(IURLGenerator::class)->disableOriginalConstructor()->getMock();
3910-
$iUrlGeneratorMock->method('getBaseUrl')->willReturn('https%3A%2F%2Fnc.my-server.org');
3911-
$service = $this->getOpenProjectAPIServiceMock(
3912-
['request'],
3913-
[
3914-
'urlGenerator' => $iUrlGeneratorMock,
3915-
],
3916-
);
3910+
$service = $this->getOpenProjectAPIServiceMock(['request']);
39173911
$service->method('request')
39183912
->with(
39193913
'user', 'work_packages/available_projects',
@@ -5197,4 +5191,52 @@ public function testGetAppsName(string $appId, ?array $appInfo, string $expected
51975191
$this->assertSame($expected, $result);
51985192
}
51995193

5194+
/**
5195+
* @return array<mixed>
5196+
*/
5197+
public function getNCBaseUrlDataProvider() {
5198+
return [
5199+
'HTTP url' => [
5200+
'url' => 'http://test.nc.local',
5201+
'expected' => 'https://test.nc.local',
5202+
],
5203+
'HTTPS url' => [
5204+
'url' => 'https://test.nc.local',
5205+
'expected' => 'https://test.nc.local',
5206+
],
5207+
'invalid url' => [
5208+
'url' => 'invalid_url',
5209+
'expected' => '',
5210+
],
5211+
'empty url' => [
5212+
'url' => '',
5213+
'expected' => '',
5214+
],
5215+
];
5216+
}
5217+
5218+
/**
5219+
* @dataProvider getNCBaseUrlDataProvider
5220+
* @param string $url
5221+
* @param string $expected
5222+
* @return void
5223+
*/
5224+
public function testGetNCBaseUrl(string $url, string $expected): void {
5225+
$configMock = $this->createMock(IConfig::class);
5226+
$configMock
5227+
->method('getSystemValueString')
5228+
->with($this->equalTo('overwrite.cli.url'))
5229+
->willReturn($url);
5230+
5231+
$service = $this->getOpenProjectAPIServiceMock(
5232+
[],
5233+
[
5234+
'config' => $configMock,
5235+
],
5236+
false,
5237+
);
5238+
5239+
$baseUrl = $service->getNCBaseUrl();
5240+
$this->assertEquals($expected, $baseUrl);
5241+
}
52005242
}

0 commit comments

Comments
 (0)