Skip to content

Commit 13ed2de

Browse files
committed
Add support for trusted apps in authorization flow.
1 parent 99f1b10 commit 13ed2de

File tree

5 files changed

+111
-9
lines changed

5 files changed

+111
-9
lines changed

init.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ php console.php app:enable solid
66
php console.php config:system:set trusted_domains 1 --value=server
77
php console.php config:system:set trusted_domains 2 --value=nextcloud.local
88
php console.php config:system:set trusted_domains 3 --value=thirdparty
9-
# set 'tester' and 'https://tester' as allowed clients for the test suite to run
10-
php console.php user:setting alice solid allowedClients '["f5d1278e8109edd94e1e4197e04873b9", "2e5cddcf0f663544e98982931e6cc5a6"]'
9+
# set 'tester' and 'https://tester' as tyrusted apps for the test suite to run
10+
php console.php config:app:set solid trustedApps --type=string --value='["https://tester", "tester"]'
11+
1112
echo configured
1213
mkdir -p /var/www/html/data/files_trashbin/versions

solid/lib/BaseServerConfig.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ public function getClientRegistration($clientId) {
198198
return json_decode($data, true);
199199
}
200200

201+
public function getTrustedApps()
202+
{
203+
$appValue = $this->config->getAppValue('solid', 'trustedApps', '[]');
204+
205+
return json_decode($appValue, true, 512, JSON_THROW_ON_ERROR);
206+
}
207+
201208
public function getUserSubDomainsEnabled() {
202209
$value = $this->config->getAppValue('solid', 'userSubDomainsEnabled', false);
203210

solid/lib/Controller/ServerController.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Lcobucci\JWT\Configuration;
1919
use Lcobucci\JWT\Signer\Key\InMemory;
2020
use Lcobucci\JWT\Signer\Rsa\Sha256;
21+
use Pdsinterop\Solid\Auth\Enum\Authorization;
2122

2223
class ServerController extends Controller
2324
{
@@ -286,13 +287,23 @@ public function authorize() {
286287
return $this->respond($response); // ->addHeader('Access-Control-Allow-Origin', '*');
287288
}
288289

289-
private function checkApproval($clientId) {
290+
private function checkApproval($clientId, $clientRegistration)
291+
{
292+
$approved = Authorization::DENIED;
293+
290294
$allowedClients = $this->config->getAllowedClients($this->userId);
291295
if (in_array($clientId, $allowedClients)) {
292-
return \Pdsinterop\Solid\Auth\Enum\Authorization::APPROVED;
293-
} else {
294-
return \Pdsinterop\Solid\Auth\Enum\Authorization::DENIED;
296+
$approved = Authorization::APPROVED;
297+
} elseif (isset($clientRegistration['origin'])) {
298+
$origin = $clientRegistration['origin'];
299+
$trustedApps = $this->config->getTrustedApps();
300+
301+
if (in_array($origin, $trustedApps)) {
302+
$approved = Authorization::APPROVED;
303+
}
295304
}
305+
306+
return $approved;
296307
}
297308

298309
private function getProfilePage() {

solid/tests/Unit/BaseServerConfigTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,27 @@ public function testRemoveClientRegistration()
208208
$baseServerConfig->removeClientRegistration(self::MOCK_CLIENT_ID);
209209
}
210210

211+
/**
212+
* @testdox BaseServerConfig should return decoded trusted apps when asked to GetTrustedApps
213+
* @covers ::getTrustedApps
214+
*/
215+
public function testGetTrustedApps()
216+
{
217+
$configMock = $this->createMock(IConfig::class);
218+
$baseServerConfig = new BaseServerConfig($configMock);
219+
220+
$expected = [self::MOCK_ORIGIN];
221+
222+
$configMock->expects($this->once())
223+
->method('getAppValue')
224+
->with(Application::APP_ID, 'trustedApps', '[]')
225+
->willReturn(json_encode($expected));
226+
227+
$actual = $baseServerConfig->getTrustedApps();
228+
229+
$this->assertEquals($expected, $actual);
230+
}
231+
211232
/////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
212233

213234
public function provideBooleans()

solid/tests/Unit/Controller/ServerControllerTest.php

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,67 @@ public function testAuthorizeWithoutApprovedClient()
189189
$this->assertEquals($expected, $actual);
190190
}
191191

192+
/**
193+
* @testdox
194+
*
195+
* @covers ::authorize
196+
*/
197+
public function testAuthorizeWithTrustedApp()
198+
{
199+
$_GET['client_id'] = self::MOCK_CLIENT_ID;
200+
$_GET['redirect_uri'] = 'https://mock.client/redirect';
201+
202+
$origin = 'https://mock.client/';
203+
$clientData = json_encode([
204+
'client_name' => 'Mock Client',
205+
'origin' => $origin,
206+
'redirect_uris' => ['https://mock.client/redirect'],
207+
], JSON_THROW_ON_ERROR);
208+
$trustedApps = json_encode([$origin], JSON_THROW_ON_ERROR);
209+
210+
$parameters = $this->createMockConstructorParameters($clientData, $trustedApps);
211+
212+
$this->mockConfig->method('getUserValue')->willReturnArgument(3);
213+
214+
$this->mockUserManager->method('userExists')->willReturn(true);
215+
216+
$controller = new ServerController(...$parameters);
217+
218+
$response = $controller->authorize();
219+
220+
$expected = $this->createExpectedResponse();
221+
222+
$actual = [
223+
'data' => $response->getData(),
224+
'headers' => $response->getHeaders(),
225+
'status' => $response->getStatus(),
226+
];
227+
228+
$location = $actual['headers']['Location'] ?? '';
229+
230+
// Not comparing time-sensitive data
231+
unset($actual['headers']['X-Request-Id'], $actual['headers']['Location']);
232+
233+
$this->assertEquals($expected, $actual);
234+
235+
// @TODO: Move $location assert to a separate test
236+
$url = parse_url($location);
237+
238+
parse_str($url['fragment'], $url['fragment']);
239+
240+
unset($url['fragment']['access_token'], $url['fragment']['id_token']);
241+
242+
$this->assertEquals([
243+
'scheme' => 'https',
244+
'host' => 'mock.client',
245+
'path' => '/redirect',
246+
'fragment' => [
247+
'token_type' => 'Bearer',
248+
'expires_in' => '3600',
249+
],
250+
], $url);
251+
}
252+
192253
/**
193254
* @testdox ServerController should return a 400 when asked to authorize a client that sends an incorrect redirect URI
194255
*
@@ -460,7 +521,7 @@ public function testToken()
460521

461522
////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\
462523

463-
public function createMockConfig($clientData): IConfig|MockObject
524+
public function createMockConfig($clientData, $trustedApps): IConfig|MockObject
464525
{
465526
$this->mockConfig = $this->createMock(IConfig::class);
466527

@@ -470,12 +531,13 @@ public function createMockConfig($clientData): IConfig|MockObject
470531
[Application::APP_ID, 'client-', '{}', 'return' => $clientData],
471532
[Application::APP_ID, 'encryptionKey', '', 'return' => 'mock encryption key'],
472533
[Application::APP_ID, 'privateKey', '', 'return' => self::$privateKey],
534+
[Application::APP_ID, 'trustedApps', '[]', 'return' => $trustedApps],
473535
]);
474536

475537
return $this->mockConfig;
476538
}
477539

478-
public function createMockConstructorParameters($clientData = '{}'): array
540+
public function createMockConstructorParameters($clientData = '{}', $trustedApps = '[]'): array
479541
{
480542
$parameters = [
481543
'mock appname',
@@ -484,7 +546,7 @@ public function createMockConstructorParameters($clientData = '{}'): array
484546
$this->createMockUserManager(),
485547
$this->createMockUrlGenerator(),
486548
self::MOCK_USER_ID,
487-
$this->createMockConfig($clientData),
549+
$this->createMockConfig($clientData, $trustedApps),
488550
$this->createMock(UserService::class),
489551
$this->createMock(IDBConnection::class),
490552
];

0 commit comments

Comments
 (0)