diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2022-08-12 16:07:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-12 16:07:52 +0200 |
commit | 91d3bd8df8a5288c2abb3badde00c248d974dae8 (patch) | |
tree | c90b1ec7d0600f7119959532167c5fbd1f2b0b8f /tests | |
parent | b6c2ad7feef446b992d087d84f0c5101a6ef5000 (diff) | |
parent | ad8134738219349269fc4d7191b2a1a205178ca9 (diff) |
Merge pull request #7472 from nextcloud/jwt-auth
Implement JWT auth for signaling connections (hello v2)
Diffstat (limited to 'tests')
-rw-r--r-- | tests/php/CapabilitiesTest.php | 17 | ||||
-rw-r--r-- | tests/php/ConfigTest.php | 214 | ||||
-rw-r--r-- | tests/php/Controller/SignalingControllerTest.php | 25 | ||||
-rw-r--r-- | tests/php/Signaling/BackendNotifierTest.php | 3 |
4 files changed, 197 insertions, 62 deletions
diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php index 39017081b..3c050c089 100644 --- a/tests/php/CapabilitiesTest.php +++ b/tests/php/CapabilitiesTest.php @@ -316,4 +316,21 @@ class CapabilitiesTest extends TestCase { $this->assertInstanceOf(IPublicCapability::class, $capabilities); $this->assertSame([], $capabilities->getCapabilities()); } + + public function testCapabilitiesHelloV2Key(): void { + $capabilities = new Capabilities( + $this->serverConfig, + $this->talkConfig, + $this->commentsManager, + $this->userSession, + $this->appManager + ); + + $this->talkConfig->expects($this->once()) + ->method('getSignalingTokenPublicKey') + ->willReturn('this-is-the-key'); + + $data = $capabilities->getCapabilities(); + $this->assertEquals('this-is-the-key', $data['spreed']['config']['signaling']['hello-v2-token-key']); + } } diff --git a/tests/php/ConfigTest.php b/tests/php/ConfigTest.php index 80fed760e..569d89177 100644 --- a/tests/php/ConfigTest.php +++ b/tests/php/ConfigTest.php @@ -20,6 +20,9 @@ */ namespace OCA\Talk\Tests\php; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; + use OCA\Talk\Config; use OCA\Talk\Events\GetTurnServersEvent; use OCA\Talk\Tests\php\Mocks\GetTurnServerListener; @@ -27,25 +30,38 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use OCP\IGroupManager; +use OCP\IURLGenerator; +use OCP\IUser; +use OCP\IUserManager; use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class ConfigTest extends TestCase { - public function testGetStunServers() { - $servers = [ - 'stun1.example.com:443', - 'stun2.example.com:129', - ]; - + private function createConfig(IConfig $config) { /** @var MockObject|ITimeFactory $timeFactory */ $timeFactory = $this->createMock(ITimeFactory::class); /** @var MockObject|ISecureRandom $secureRandom */ $secureRandom = $this->createMock(ISecureRandom::class); - /** @var MockObject|IGroupManager $secureRandom */ + /** @var MockObject|IGroupManager $groupManager */ $groupManager = $this->createMock(IGroupManager::class); + /** @var MockObject|IUserManager $userManager */ + $userManager = $this->createMock(IUserManager::class); + /** @var MockObject|IURLGenerator $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); /** @var MockObject|IEventDispatcher $dispatcher */ $dispatcher = $this->createMock(IEventDispatcher::class); + + $helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher); + return $helper; + } + + public function testGetStunServers() { + $servers = [ + 'stun1.example.com:443', + 'stun2.example.com:129', + ]; + /** @var MockObject|IConfig $config */ $config = $this->createMock(IConfig::class); $config @@ -59,19 +75,11 @@ class ConfigTest extends TestCase { ->with('has_internet_connection', true) ->willReturn(true); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = $this->createConfig($config); $this->assertSame($helper->getStunServers(), $servers); } public function testGetDefaultStunServer() { - /** @var MockObject|ITimeFactory $timeFactory */ - $timeFactory = $this->createMock(ITimeFactory::class); - /** @var MockObject|ISecureRandom $secureRandom */ - $secureRandom = $this->createMock(ISecureRandom::class); - /** @var MockObject|IGroupManager $secureRandom */ - $groupManager = $this->createMock(IGroupManager::class); - /** @var MockObject|IEventDispatcher $dispatcher */ - $dispatcher = $this->createMock(IEventDispatcher::class); /** @var MockObject|IConfig $config */ $config = $this->createMock(IConfig::class); $config @@ -85,19 +93,11 @@ class ConfigTest extends TestCase { ->with('has_internet_connection', true) ->willReturn(true); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = $this->createConfig($config); $this->assertSame(['stun.nextcloud.com:443'], $helper->getStunServers()); } public function testGetDefaultStunServerNoInternet() { - /** @var MockObject|ITimeFactory $timeFactory */ - $timeFactory = $this->createMock(ITimeFactory::class); - /** @var MockObject|ISecureRandom $secureRandom */ - $secureRandom = $this->createMock(ISecureRandom::class); - /** @var MockObject|IGroupManager $secureRandom */ - $groupManager = $this->createMock(IGroupManager::class); - /** @var MockObject|IEventDispatcher $dispatcher */ - $dispatcher = $this->createMock(IEventDispatcher::class); /** @var MockObject|IConfig $config */ $config = $this->createMock(IConfig::class); $config @@ -111,7 +111,7 @@ class ConfigTest extends TestCase { ->with('has_internet_connection', true) ->willReturn(false); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = $this->createConfig($config); $this->assertSame([], $helper->getStunServers()); } @@ -151,8 +151,12 @@ class ConfigTest extends TestCase { ->method('getTime') ->willReturn(1479743025); - /** @var MockObject|IGroupManager $secureRandom */ + /** @var MockObject|IGroupManager $groupManager */ $groupManager = $this->createMock(IGroupManager::class); + /** @var MockObject|IUserManager $userManager */ + $userManager = $this->createMock(IUserManager::class); + /** @var MockObject|IURLGenerator $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); /** @var MockObject|IEventDispatcher $dispatcher */ $dispatcher = $this->createMock(IEventDispatcher::class); @@ -163,7 +167,7 @@ class ConfigTest extends TestCase { ->method('generate') ->with(16) ->willReturn('abcdefghijklmnop'); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher); // $settings = $helper->getTurnSettings(); @@ -200,19 +204,7 @@ class ConfigTest extends TestCase { ->with('spreed', 'turn_servers', '') ->willReturn(json_encode([])); - /** @var MockObject|ITimeFactory $timeFactory */ - $timeFactory = $this->createMock(ITimeFactory::class); - - /** @var MockObject|IGroupManager $secureRandom */ - $groupManager = $this->createMock(IGroupManager::class); - - /** @var MockObject|ISecureRandom $secureRandom */ - $secureRandom = $this->createMock(ISecureRandom::class); - - /** @var MockObject|IEventDispatcher $dispatcher */ - $dispatcher = $this->createMock(IEventDispatcher::class); - - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = $this->createConfig($config); $settings = $helper->getTurnSettings(); $this->assertEquals(0, count($settings)); @@ -230,9 +222,15 @@ class ConfigTest extends TestCase { /** @var MockObject|ITimeFactory $timeFactory */ $timeFactory = $this->createMock(ITimeFactory::class); - /** @var MockObject|IGroupManager $secureRandom */ + /** @var MockObject|IGroupManager $groupManager */ $groupManager = $this->createMock(IGroupManager::class); + /** @var MockObject|IUserManager $userManager */ + $userManager = $this->createMock(IUserManager::class); + + /** @var MockObject|IURLGenerator $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); + /** @var MockObject|ISecureRandom $secureRandom */ $secureRandom = $this->createMock(ISecureRandom::class); @@ -258,7 +256,7 @@ class ConfigTest extends TestCase { $dispatcher->addServiceListener(GetTurnServersEvent::class, GetTurnServerListener::class); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher); $settings = $helper->getTurnSettings(); $this->assertSame($servers, $settings); @@ -326,22 +324,136 @@ class ConfigTest extends TestCase { * @param string $expectedWebSocketDomain */ public function testGetWebSocketDomainForSignalingServer($url, $expectedWebSocketDomain) { + /** @var MockObject|IConfig $config */ + $config = $this->createMock(IConfig::class); + + $helper = $this->createConfig($config); + + $this->assertEquals( + $expectedWebSocketDomain, + self::invokePrivate($helper, 'getWebSocketDomainForSignalingServer', [$url]) + ); + } + + public function dataTicketV2Algorithm() { + return [ + ['ES384'], + ['ES256'], + ['RS256'], + ['RS384'], + ['RS512'], + ['EdDSA'], + ]; + } + + /** + * @dataProvider dataTicketV2Algorithm + * @param string $algo + */ + public function testSignalingTicketV2User(string $algo): void { + /** @var IConfig $config */ + $config = \OC::$server->getConfig(); /** @var MockObject|ITimeFactory $timeFactory */ $timeFactory = $this->createMock(ITimeFactory::class); /** @var MockObject|ISecureRandom $secureRandom */ $secureRandom = $this->createMock(ISecureRandom::class); - /** @var MockObject|IGroupManager $secureRandom */ + /** @var MockObject|IGroupManager $groupManager */ $groupManager = $this->createMock(IGroupManager::class); + /** @var MockObject|IUserManager $userManager */ + $userManager = $this->createMock(IUserManager::class); + /** @var MockObject|IURLGenerator $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); /** @var MockObject|IEventDispatcher $dispatcher */ $dispatcher = $this->createMock(IEventDispatcher::class); - /** @var MockObject|IConfig $config */ - $config = $this->createMock(IConfig::class); + /** @var MockObject|IUser $user */ + $user = $this->createMock(IUser::class); - $helper = new Config($config, $secureRandom, $groupManager, $timeFactory, $dispatcher); + $now = time(); + $timeFactory + ->expects($this->once()) + ->method('getTime') + ->willReturn($now); + $urlGenerator + ->expects($this->once()) + ->method('getAbsoluteURL') + ->with('') + ->willReturn('https://domain.invalid/nextcloud'); + $userManager + ->expects($this->once()) + ->method('get') + ->with('user1') + ->willReturn($user); + $user + ->expects($this->once()) + ->method('getUID') + ->willReturn('user1'); + $user + ->expects($this->once()) + ->method('getDisplayName') + ->willReturn('Jane Doe'); - $this->assertEquals( - $expectedWebSocketDomain, - self::invokePrivate($helper, 'getWebSocketDomainForSignalingServer', [$url]) - ); + $helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher); + + $config->setAppValue('spreed', 'signaling_token_alg', $algo); + // Make sure new keys are generated. + $config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo)); + $config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)); + $ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, 'user1'); + $this->assertNotNull($ticket); + + $key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo); + $decoded = JWT::decode($ticket, $key); + + $this->assertEquals($now, $decoded->iat); + $this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss); + $this->assertEquals('user1', $decoded->sub); + $this->assertSame(['displayname' => 'Jane Doe'], (array) $decoded->userdata); + } + + /** + * @dataProvider dataTicketV2Algorithm + * @param string $algo + */ + public function testSignalingTicketV2Anonymous(string $algo): void { + /** @var IConfig $config */ + $config = \OC::$server->getConfig(); + /** @var MockObject|ITimeFactory $timeFactory */ + $timeFactory = $this->createMock(ITimeFactory::class); + /** @var MockObject|ISecureRandom $secureRandom */ + $secureRandom = $this->createMock(ISecureRandom::class); + /** @var MockObject|IGroupManager $groupManager */ + $groupManager = $this->createMock(IGroupManager::class); + /** @var MockObject|IUserManager $userManager */ + $userManager = $this->createMock(IUserManager::class); + /** @var MockObject|IURLGenerator $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); + /** @var MockObject|IEventDispatcher $dispatcher */ + $dispatcher = $this->createMock(IEventDispatcher::class); + + $now = time(); + $timeFactory + ->expects($this->once()) + ->method('getTime') + ->willReturn($now); + $urlGenerator + ->expects($this->once()) + ->method('getAbsoluteURL') + ->with('') + ->willReturn('https://domain.invalid/nextcloud'); + + $helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher); + + $config->setAppValue('spreed', 'signaling_token_alg', $algo); + // Make sure new keys are generated. + $config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo)); + $config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)); + $ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, null); + $this->assertNotNull($ticket); + + $key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo); + $decoded = JWT::decode($ticket, $key); + + $this->assertEquals($now, $decoded->iat); + $this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss); } } diff --git a/tests/php/Controller/SignalingControllerTest.php b/tests/php/Controller/SignalingControllerTest.php index 1b6329d26..e74cddd73 100644 --- a/tests/php/Controller/SignalingControllerTest.php +++ b/tests/php/Controller/SignalingControllerTest.php @@ -43,10 +43,12 @@ use OCP\App\IAppManager; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; use OCP\Http\Client\IClientService; +use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; +use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\Security\IHasher; @@ -71,6 +73,7 @@ class CustomInputSignalingController extends SignalingController { * @group DB */ class SignalingControllerTest extends TestCase { + private IConfig $serverConfig; private ?Config $config = null; /** @var TalkSession|MockObject */ private $session; @@ -107,14 +110,16 @@ class SignalingControllerTest extends TestCase { $this->secureRandom = \OC::$server->getSecureRandom(); $timeFactory = $this->createMock(ITimeFactory::class); $groupManager = $this->createMock(IGroupManager::class); - $config = \OC::$server->getConfig(); - $config->setAppValue('spreed', 'signaling_servers', json_encode([ + $this->serverConfig = \OC::$server->getConfig(); + $this->serverConfig->setAppValue('spreed', 'signaling_servers', json_encode([ 'secret' => 'MySecretValue', ])); - $config->setAppValue('spreed', 'signaling_ticket_secret', 'the-app-ticket-secret'); - $config->setUserValue($this->userId, 'spreed', 'signaling_ticket_secret', 'the-user-ticket-secret'); + $this->serverConfig->setAppValue('spreed', 'signaling_ticket_secret', 'the-app-ticket-secret'); + $this->serverConfig->setUserValue($this->userId, 'spreed', 'signaling_ticket_secret', 'the-user-ticket-secret'); + $this->userManager = $this->createMock(IUserManager::class); $this->dispatcher = \OC::$server->get(IEventDispatcher::class); - $this->config = new Config($config, $this->secureRandom, $groupManager, $timeFactory, $this->dispatcher); + $urlGenerator = $this->createMock(IURLGenerator::class); + $this->config = new Config($this->serverConfig, $this->secureRandom, $groupManager, $this->userManager, $urlGenerator, $timeFactory, $this->dispatcher); $this->session = $this->createMock(TalkSession::class); $this->dbConnection = \OC::$server->getDatabaseConnection(); $this->signalingManager = $this->createMock(\OCA\Talk\Signaling\Manager::class); @@ -122,7 +127,6 @@ class SignalingControllerTest extends TestCase { $this->participantService = $this->createMock(ParticipantService::class); $this->sessionService = $this->createMock(SessionService::class); $this->messages = $this->createMock(Messages::class); - $this->userManager = $this->createMock(IUserManager::class); $this->timeFactory = $this->createMock(ITimeFactory::class); $this->clientService = $this->createMock(IClientService::class); $this->logger = $this->createMock(LoggerInterface::class); @@ -133,6 +137,7 @@ class SignalingControllerTest extends TestCase { $this->controller = new CustomInputSignalingController( 'spreed', $this->createMock(IRequest::class), + $this->serverConfig, $this->config, $this->signalingManager, $this->session, @@ -273,7 +278,7 @@ class SignalingControllerTest extends TestCase { 'auth' => [ 'params' => [ 'userid' => 'invalid-userid', - 'ticket' => $this->config->getSignalingTicket($this->userId), + 'ticket' => $this->config->getSignalingTicket(Config::SIGNALING_TICKET_V1, $this->userId), ], ], ]); @@ -291,7 +296,7 @@ class SignalingControllerTest extends TestCase { 'auth' => [ 'params' => [ 'userid' => 'unknown-userid', - 'ticket' => $this->config->getSignalingTicket('unknown-userid'), + 'ticket' => $this->config->getSignalingTicket(Config::SIGNALING_TICKET_V1, 'unknown-userid'), ], ], ]); @@ -320,7 +325,7 @@ class SignalingControllerTest extends TestCase { 'auth' => [ 'params' => [ 'userid' => $this->userId, - 'ticket' => $this->config->getSignalingTicket($this->userId), + 'ticket' => $this->config->getSignalingTicket(Config::SIGNALING_TICKET_V1, $this->userId), ], ], ]); @@ -341,7 +346,7 @@ class SignalingControllerTest extends TestCase { 'auth' => [ 'params' => [ 'userid' => '', - 'ticket' => $this->config->getSignalingTicket(''), + 'ticket' => $this->config->getSignalingTicket(Config::SIGNALING_TICKET_V1, ''), ], ], ]); diff --git a/tests/php/Signaling/BackendNotifierTest.php b/tests/php/Signaling/BackendNotifierTest.php index 4f82ff5e4..cce3251c8 100644 --- a/tests/php/Signaling/BackendNotifierTest.php +++ b/tests/php/Signaling/BackendNotifierTest.php @@ -113,6 +113,7 @@ class BackendNotifierTest extends TestCase { $this->timeFactory = $this->createMock(ITimeFactory::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); $groupManager = $this->createMock(IGroupManager::class); + $userManager = $this->createMock(IUserManager::class); $config = \OC::$server->getConfig(); $this->signalingSecret = 'the-signaling-secret'; $this->baseUrl = 'https://localhost/signaling'; @@ -132,7 +133,7 @@ class BackendNotifierTest extends TestCase { ->willReturn(['server' => $this->baseUrl]); $this->dispatcher = \OC::$server->get(IEventDispatcher::class); - $this->config = new Config($config, $this->secureRandom, $groupManager, $this->timeFactory, $this->dispatcher); + $this->config = new Config($config, $this->secureRandom, $groupManager, $userManager, $this->urlGenerator, $this->timeFactory, $this->dispatcher); $this->recreateBackendNotifier(); $this->overwriteService(BackendNotifier::class, $this->controller); |