Skip to content

Commit 3500fcf

Browse files
committed
Add unit tests for ServerController::auth().
1 parent f728f3f commit 3500fcf

1 file changed

Lines changed: 183 additions & 34 deletions

File tree

solid/tests/Unit/Controller/ServerControllerTest.php

Lines changed: 183 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,46 @@
33
namespace OCA\Solid\Controller\ServerController;
44

55
use OCA\Solid\AppInfo\Application;
6-
use OCA\Solid\BaseServerConfig;
76
use OCA\Solid\Controller\ServerController;
87
use OCA\Solid\Service\UserService;
8+
use OCP\AppFramework\Http\JSONResponse;
99
use OCP\IConfig;
1010
use OCP\IDBConnection;
1111
use OCP\IRequest;
1212
use OCP\ISession;
1313
use OCP\IURLGenerator;
1414
use OCP\IUserManager;
15+
use PHPUnit\Framework\MockObject\MockObject;
1516
use PHPUnit\Framework\TestCase;
1617

1718
/**
1819
* @coversDefaultClass \OCA\Solid\Controller\ServerController
1920
* @covers ::__construct
2021
*
2122
* @uses \OCA\Solid\Controller\ServerController
23+
* @uses \OCA\Solid\BaseServerConfig
24+
* @uses \OCA\Solid\JtiReplayDetector
25+
* @uses \OCA\Solid\ServerConfig
2226
*/
2327
class ServerControllerTest extends TestCase
2428
{
2529
////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
2630

2731
private const MOCK_CLIENT_ID = 'mock-client-id';
28-
private static string $encryptionKey;
32+
private const MOCK_USER_ID = 'mock user id';
33+
2934
private static string $privateKey;
30-
private static string $publicKey;
35+
36+
private IConfig|MockObject $mockConfig;
37+
private IUserManager|MockObject $mockUserManager;
3138

3239

3340
public static function setUpBeforeClass(): void
3441
{
3542
$keyPath = __DIR__ . '/../../fixtures/keys';
3643
self::$privateKey = file_get_contents($keyPath . '/private.key');
3744
}
45+
3846
/////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
3947

4048
/**
@@ -44,19 +52,9 @@ public static function setUpBeforeClass(): void
4452
*
4553
* @dataProvider provideConstructorParameterIndex
4654
*/
47-
public function testInstatiationWithoutRequiredParameter($index)
55+
public function testInstantiationWithoutRequiredParameter($index)
4856
{
49-
$parameters = [
50-
'mock appname',
51-
$this->createMock(IRequest::class),
52-
$this->createMock(ISession::class),
53-
$this->createMock(IUserManager::class),
54-
$this->createMock(IURLGenerator::class),
55-
'mock user id',
56-
$this->createMock(IConfig::class),
57-
$this->createMock(UserService::class),
58-
$this->createMock(IDBConnection::class),
59-
];
57+
$parameters = $this->createMockConstructorParameters();
6058

6159
$parameters = array_slice($parameters, 0, $index);
6260

@@ -83,44 +81,195 @@ public function testInstatiationWithoutRequiredParameter($index)
8381
* @testdox ServerController should be instantiable with all required parameters
8482
*
8583
* @covers ::__construct
84+
*/
85+
public function testInstantiation()
86+
{
87+
$parameters = $this->createMockConstructorParameters();
88+
89+
$controller = new ServerController(...$parameters);
90+
91+
$this->assertInstanceOf(ServerController::class, $controller);
92+
}
93+
94+
/**
95+
* @testdox ServerController should return a 401 when asked to authorize without signed-in user
96+
*
97+
* @covers ::authorize
98+
*/
99+
public function testAuthorizeWithoutUser()
100+
{
101+
$parameters = $this->createMockConstructorParameters();
102+
103+
$controller = new ServerController(...$parameters);
104+
105+
$expected = new JSONResponse('Authorization required', 401);
106+
$actual = $controller->authorize();
107+
108+
$this->assertEquals($expected, $actual);
109+
}
110+
111+
/**
112+
* @testdox ServerController should return a 400 when asked to authorize with a user but without valid token
113+
*
114+
* @covers ::authorize
115+
*/
116+
public function testAuthorizeWithoutValidToken()
117+
{
118+
$_GET['response_type'] = 'mock-response-type';
119+
120+
$parameters = $this->createMockConstructorParameters();
121+
122+
$this->mockUserManager->method('userExists')->willReturn(true);
123+
124+
$controller = new ServerController(...$parameters);
125+
126+
$actual = $controller->authorize();
127+
$expected = new JSONResponse('Bad request, does not contain valid token', 400);
128+
129+
$this->assertEquals($expected, $actual);
130+
}
131+
132+
/**
133+
* @testdox ServerController should return a 302 redirect when asked to authorize client that has not been approved
134+
*
135+
* @covers ::authorize
136+
*/
137+
public function testAuthorizeWithoutApprovedClient()
138+
{
139+
$_GET['client_id'] = self::MOCK_CLIENT_ID;
140+
$_GET['nonce'] = 'mock-nonce';
141+
// JWT with empty payload, HS256 encoded, created with `private.key` from fixtures
142+
$_GET['request'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.8VKCTiBegJPuPIZlp0wbV0Sbdn5BS6TE5DCx6oYNc5o';
143+
$_GET['response_type'] = 'mock-response-type';
144+
145+
$_SERVER['REQUEST_URI'] = 'mock uri';
146+
147+
$parameters = $this->createMockConstructorParameters();
148+
149+
$this->mockConfig->method('getUserValue')->willReturnArgument(3);
150+
151+
$this->mockUserManager->method('userExists')->willReturn(true);
152+
153+
$controller = new ServerController(...$parameters);
154+
155+
$actual = $controller->authorize();
156+
$expected = new JSONResponse('Approval required', 302, ['Location' => '']);
157+
158+
$this->assertEquals($expected, $actual);
159+
}
160+
161+
/**
162+
* @testdox ServerController should return a 302 redirect when asked to authorize client that has been approved
86163
*
87-
* @uses \League\OAuth2\Server\AuthorizationServer
88-
* @uses \OCA\Solid\BaseServerConfig
89-
* @uses \OCA\Solid\JtiReplayDetector
90-
* @uses \OCA\Solid\ServerConfig
91-
* @uses \Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory
92-
* @uses \Pdsinterop\Solid\Auth\Factory\GrantTypeFactory
93-
* @uses \Pdsinterop\Solid\Auth\Factory\RepositoryFactory
94-
* @uses \Pdsinterop\Solid\Auth\TokenGenerator
164+
* @covers ::authorize
95165
*/
96-
public function testInstatiation()
166+
public function testAuthorizeWithApprovedClient()
97167
{
98-
$configMock = $this->createMock(IConfig::class);
168+
$_GET['client_id'] = self::MOCK_CLIENT_ID;
169+
$_GET['nonce'] = 'mock-nonce';
170+
// JWT with empty payload, HS256 encoded, created with `private.key` from fixtures
171+
$_GET['request'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.8VKCTiBegJPuPIZlp0wbV0Sbdn5BS6TE5DCx6oYNc5o';
172+
$_GET['response_type'] = 'mock-response-type';
173+
174+
$_SERVER['REQUEST_URI'] = 'https://mock.server';
175+
176+
$clientData = json_encode(['client_name' => 'Mock Client', 'redirect_uris' => ['https://mock.client/redirect']]);
177+
178+
$parameters = $this->createMockConstructorParameters($clientData);
179+
180+
$this->mockConfig->method('getUserValue')
181+
->with(self::MOCK_USER_ID, Application::APP_ID, 'allowedClients', '[]')
182+
->willReturn(json_encode([self::MOCK_CLIENT_ID]));
183+
184+
$this->mockUserManager->method('userExists')->willReturn(true);
185+
186+
$controller = new ServerController(...$parameters);
187+
188+
$response = $controller->authorize();
189+
190+
$expected = [
191+
'data' => 'ok',
192+
'headers' => [
193+
'Cache-Control' => 'no-cache, no-store, must-revalidate',
194+
'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'",
195+
'Feature-Policy' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'",
196+
'X-Robots-Tag' => 'noindex, nofollow',
197+
'Content-Type' => 'application/json; charset=utf-8',
99198

100-
$configMock->method('getAppValue')->willReturnMap([
101-
[Application::APP_ID, 'client-' . self::MOCK_CLIENT_ID, '{}', 'return' => '{}'],
102-
[Application::APP_ID, 'client-d6d7896757f61ac4c397d914053180ff', '{}', 'return' => '{}'],
103-
[Application::APP_ID, 'client-', '{}', 'return' => '{}'],
199+
],
200+
'status' => 302,
201+
];
202+
203+
$headers = $response->getHeaders();
204+
$location = $headers['Location'];
205+
// Not comparing time-sensitive data
206+
unset($headers['X-Request-Id'], $headers['Location']);
207+
208+
$actual = [
209+
'data' => $response->getData(),
210+
'headers' => $headers,
211+
'status' => $response->getStatus(),
212+
];
213+
214+
$this->assertEquals($expected, $actual);
215+
216+
// @TODO: Move $location assert to a separate test
217+
$url = parse_url($location);
218+
219+
parse_str($url['fragment'], $url['fragment']);
220+
221+
unset($url['fragment']['access_token'], $url['fragment']['id_token']);
222+
223+
$this->assertEquals([
224+
'scheme' => 'https',
225+
'host' => 'mock.client',
226+
'path' => '/redirect',
227+
'fragment' => [
228+
'token_type'=>'Bearer',
229+
'expires_in'=>'3600',
230+
],
231+
], $url);
232+
}
233+
234+
////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\
235+
236+
public function createMockConfig($clientData): IConfig|MockObject
237+
{
238+
$this->mockConfig = $this->createMock(IConfig::class);
239+
240+
$this->mockConfig->method('getAppValue')->willReturnMap([
241+
[Application::APP_ID, 'client-' . self::MOCK_CLIENT_ID, '{}', 'return' => $clientData],
242+
[Application::APP_ID, 'client-d6d7896757f61ac4c397d914053180ff', '{}', 'return' => $clientData],
243+
[Application::APP_ID, 'client-', '{}', 'return' => $clientData],
104244
[Application::APP_ID, 'profileData', '', 'return' => ''],
105245
[Application::APP_ID, 'encryptionKey', '', 'return' => 'mock encryption key'],
106246
[Application::APP_ID, 'privateKey', '', 'return' => self::$privateKey],
107247
]);
108248

249+
return $this->mockConfig;
250+
}
251+
public function createMockConstructorParameters($clientData = '{}'): array
252+
{
109253
$parameters = [
110254
'mock appname',
111255
$this->createMock(IRequest::class),
112256
$this->createMock(ISession::class),
113-
$this->createMock(IUserManager::class),
257+
$this->createMockUserManager(),
114258
$this->createMock(IURLGenerator::class),
115-
'mock user id',
116-
$configMock,
259+
self::MOCK_USER_ID,
260+
$this->createMockConfig($clientData),
117261
$this->createMock(UserService::class),
118262
$this->createMock(IDBConnection::class),
119263
];
120264

121-
$controller = new ServerController(...$parameters);
265+
return $parameters;
266+
}
122267

123-
$this->assertInstanceOf(ServerController::class, $controller);
268+
public function createMockUserManager(): IUserManager|MockObject
269+
{
270+
$this->mockUserManager = $this->createMock(IUserManager::class);
271+
272+
return $this->mockUserManager;
124273
}
125274

126275
/////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

0 commit comments

Comments
 (0)