From b578a1e8b56f6b3ecf7dee837af6bd8265f9c0b0 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 20 Oct 2021 10:29:45 +0200 Subject: Fair use of push notifications We want to keep offering our push notification service for free, but large users overload our infrastructure. For this reason we have to rate-limit the use of push notifications. If you need this feature, consider setting up your own push server or using Nextcloud Enterprise. Signed-off-by: Joas Schilling --- tests/lib/Notification/ManagerTest.php | 86 +++++++++++++++++++++++-- tests/lib/Support/Subscription/RegistryTest.php | 9 ++- 2 files changed, 86 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/lib/Notification/ManagerTest.php b/tests/lib/Notification/ManagerTest.php index b1201d31c42..400ae3a53ef 100644 --- a/tests/lib/Notification/ManagerTest.php +++ b/tests/lib/Notification/ManagerTest.php @@ -1,4 +1,6 @@ * @@ -25,11 +27,16 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\Bootstrap\ServiceRegistration; use OC\Notification\Manager; -use OCP\ILogger; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IUserManager; use OCP\Notification\IManager; use OCP\Notification\INotification; use OCP\RichObjectStrings\IValidator; +use OCP\Support\Subscription\IRegistry; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Test\TestCase; class ManagerTest extends TestCase { @@ -38,7 +45,17 @@ class ManagerTest extends TestCase { /** @var IValidator|MockObject */ protected $validator; - /** @var ILogger|MockObject */ + /** @var IUserManager|MockObject */ + protected $userManager; + /** @var ICacheFactory|MockObject */ + protected $cacheFactory; + /** @var ICache|MockObject */ + protected $cache; + /** @var ITimeFactory|MockObject */ + protected $timeFactory; + /** @var IRegistry|MockObject */ + protected $subscriptionRegistry; + /** @var LoggerInterface|MockObject */ protected $logger; /** @var Coordinator|MockObject */ protected $coordinator; @@ -49,14 +66,23 @@ class ManagerTest extends TestCase { parent::setUp(); $this->validator = $this->createMock(IValidator::class); - $this->logger = $this->createMock(ILogger::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->cache = $this->createMock(ICache::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->subscriptionRegistry = $this->createMock(IRegistry::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createDistributed') + ->with('notifications') + ->willReturn($this->cache); $this->registrationContext = $this->createMock(RegistrationContext::class); $this->coordinator = $this->createMock(Coordinator::class); $this->coordinator->method('getRegistrationContext') ->willReturn($this->registrationContext); - $this->manager = new Manager($this->validator, $this->logger, $this->coordinator); + $this->manager = new Manager($this->validator, $this->userManager, $this->cacheFactory, $this->timeFactory, $this->subscriptionRegistry, $this->logger, $this->coordinator); } public function testRegisterApp() { @@ -128,6 +154,10 @@ class ManagerTest extends TestCase { $manager = $this->getMockBuilder(Manager::class) ->setConstructorArgs([ $this->validator, + $this->userManager, + $this->cacheFactory, + $this->timeFactory, + $this->subscriptionRegistry, $this->logger, $this->coordinator, ]) @@ -156,6 +186,10 @@ class ManagerTest extends TestCase { $manager = $this->getMockBuilder(Manager::class) ->setConstructorArgs([ $this->validator, + $this->userManager, + $this->cacheFactory, + $this->timeFactory, + $this->subscriptionRegistry, $this->logger, $this->coordinator, ]) @@ -177,6 +211,10 @@ class ManagerTest extends TestCase { $manager = $this->getMockBuilder(Manager::class) ->setConstructorArgs([ $this->validator, + $this->userManager, + $this->cacheFactory, + $this->timeFactory, + $this->subscriptionRegistry, $this->logger, $this->coordinator, ]) @@ -199,6 +237,10 @@ class ManagerTest extends TestCase { $manager = $this->getMockBuilder(Manager::class) ->setConstructorArgs([ $this->validator, + $this->userManager, + $this->cacheFactory, + $this->timeFactory, + $this->subscriptionRegistry, $this->logger, $this->coordinator, ]) @@ -211,4 +253,40 @@ class ManagerTest extends TestCase { $manager->getCount($notification); } + + public function dataIsFairUseOfFreePushService() { + return [ + // Before 1st March + [1646089199, true, 4999, true], + [1646089199, true, 5000, true], + [1646089199, false, 4999, true], + [1646089199, false, 5000, true], + + // After 1st March + [1646089200, true, 4999, true], + [1646089200, true, 5000, true], + [1646089200, false, 4999, true], + [1646089200, false, 5000, false], + ]; + } + + /** + * @dataProvider dataIsFairUseOfFreePushService + * @param int $time + * @param bool $hasValidSubscription + * @param int $userCount + * @param bool $isFair + */ + public function testIsFairUseOfFreePushService(int $time, bool $hasValidSubscription, int $userCount, bool $isFair): void { + $this->timeFactory->method('getTime') + ->willReturn($time); + + $this->subscriptionRegistry->method('delegateHasValidSubscription') + ->willReturn($hasValidSubscription); + + $this->userManager->method('countSeenUsers') + ->willReturn($userCount); + + $this->assertSame($isFair, $this->manager->isFairUseOfFreePushService()); + } } diff --git a/tests/lib/Support/Subscription/RegistryTest.php b/tests/lib/Support/Subscription/RegistryTest.php index 5349b041d8b..260232ac95d 100644 --- a/tests/lib/Support/Subscription/RegistryTest.php +++ b/tests/lib/Support/Subscription/RegistryTest.php @@ -75,8 +75,7 @@ class RegistryTest extends TestCase { $this->serverContainer, $this->userManager, $this->groupManager, - $this->logger, - $this->notificationManager + $this->logger ); } @@ -177,7 +176,7 @@ class RegistryTest extends TestCase { ->method('get') ->willReturn($dummyGroup); - $this->assertSame(true, $this->registry->delegateIsHardUserLimitReached()); + $this->assertSame(true, $this->registry->delegateIsHardUserLimitReached($this->notificationManager)); } public function testDelegateIsHardUserLimitReachedWithoutSupportApp() { @@ -186,7 +185,7 @@ class RegistryTest extends TestCase { ->with('one-click-instance') ->willReturn(false); - $this->assertSame(false, $this->registry->delegateIsHardUserLimitReached()); + $this->assertSame(false, $this->registry->delegateIsHardUserLimitReached($this->notificationManager)); } public function dataForUserLimitCheck() { @@ -237,6 +236,6 @@ class RegistryTest extends TestCase { ->willReturn($dummyGroup); } - $this->assertSame($expectedResult, $this->registry->delegateIsHardUserLimitReached()); + $this->assertSame($expectedResult, $this->registry->delegateIsHardUserLimitReached($this->notificationManager)); } } -- cgit v1.2.3 From d613b320451516c466fca7b408414a0193139602 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Wed, 20 Oct 2021 14:45:33 -0300 Subject: add check isFairUseOfFreePushService on login Signed-off-by: Vitor Mattos --- core/Controller/LoginController.php | 18 ++++++++++++++- tests/Core/Controller/LoginControllerTest.php | 32 +++++++++++++++++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index 15ec8365c19..b68f91f986e 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -46,6 +46,7 @@ use OCP\AppFramework\Http\TemplateResponse; use OCP\Defaults; use OCP\IConfig; use OCP\IInitialStateService; +use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCP\ISession; @@ -53,6 +54,7 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Notification\IManager; use OCP\Util; class LoginController extends Controller { @@ -81,6 +83,10 @@ class LoginController extends Controller { private $initialStateService; /** @var WebAuthnManager */ private $webAuthnManager; + /** @var IManager */ + private $manager; + /** @var IL10N */ + private $l10n; public function __construct(?string $appName, IRequest $request, @@ -94,7 +100,9 @@ class LoginController extends Controller { Throttler $throttler, Chain $loginChain, IInitialStateService $initialStateService, - WebAuthnManager $webAuthnManager) { + WebAuthnManager $webAuthnManager, + IManager $manager, + IL10N $l10n) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->config = $config; @@ -107,6 +115,8 @@ class LoginController extends Controller { $this->loginChain = $loginChain; $this->initialStateService = $initialStateService; $this->webAuthnManager = $webAuthnManager; + $this->manager = $manager; + $this->l10n = $l10n; } /** @@ -153,6 +163,12 @@ class LoginController extends Controller { } $loginMessages = $this->session->get('loginMessages'); + if (!$this->manager->isFairUseOfFreePushService()) { + if (!is_array($loginMessages)) { + $loginMessages = [[], []]; + } + $loginMessages[1][] = $this->l10n->t('This community release of Nextcloud is unsupported and instant notifications are unavailable.'); + } if (is_array($loginMessages)) { [$errors, $messages] = $loginMessages; $this->initialStateService->provideInitialState('core', 'loginMessages', $messages); diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index fe42b55db15..30a625a612b 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -33,12 +33,14 @@ use OCP\AppFramework\Http\TemplateResponse; use OCP\Defaults; use OCP\IConfig; use OCP\IInitialStateService; +use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; +use OCP\Notification\IManager; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -86,6 +88,12 @@ class LoginControllerTest extends TestCase { /** @var \OC\Authentication\WebAuthn\Manager|MockObject */ private $webAuthnManager; + /** @var IManager|MockObject */ + private $notificationManager; + + /** @var IL10N|MockObject */ + private $l; + protected function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); @@ -101,6 +109,13 @@ class LoginControllerTest extends TestCase { $this->chain = $this->createMock(LoginChain::class); $this->initialStateService = $this->createMock(IInitialStateService::class); $this->webAuthnManager = $this->createMock(\OC\Authentication\WebAuthn\Manager::class); + $this->notificationManager = $this->createMock(IManager::class); + $this->l = $this->createMock(IL10N::class); + $this->l->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + }); $this->request->method('getRemoteAddress') @@ -124,7 +139,9 @@ class LoginControllerTest extends TestCase { $this->throttler, $this->chain, $this->initialStateService, - $this->webAuthnManager + $this->webAuthnManager, + $this->notificationManager, + $this->l ); } @@ -249,6 +266,7 @@ class LoginControllerTest extends TestCase { [ 'MessageArray1', 'MessageArray2', + 'This community release of Nextcloud is unsupported and instant notifications are unavailable.', ] ); $this->initialStateService->expects($this->at(1)) @@ -278,7 +296,7 @@ class LoginControllerTest extends TestCase { ->expects($this->once()) ->method('isLoggedIn') ->willReturn(false); - $this->initialStateService->expects($this->at(2)) + $this->initialStateService->expects($this->at(4)) ->method('provideInitialState') ->with( 'core', @@ -339,14 +357,14 @@ class LoginControllerTest extends TestCase { ->method('get') ->with('LdapUser') ->willReturn($user); - $this->initialStateService->expects($this->at(0)) + $this->initialStateService->expects($this->at(2)) ->method('provideInitialState') ->with( 'core', 'loginUsername', 'LdapUser' ); - $this->initialStateService->expects($this->at(4)) + $this->initialStateService->expects($this->at(6)) ->method('provideInitialState') ->with( 'core', @@ -386,21 +404,21 @@ class LoginControllerTest extends TestCase { ->method('get') ->with('0') ->willReturn($user); - $this->initialStateService->expects($this->at(1)) + $this->initialStateService->expects($this->at(3)) ->method('provideInitialState') ->with( 'core', 'loginAutocomplete', true ); - $this->initialStateService->expects($this->at(3)) + $this->initialStateService->expects($this->at(5)) ->method('provideInitialState') ->with( 'core', 'loginResetPasswordLink', false ); - $this->initialStateService->expects($this->at(4)) + $this->initialStateService->expects($this->at(6)) ->method('provideInitialState') ->with( 'core', -- cgit v1.2.3