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 --- build/psalm-baseline.xml | 1 + lib/private/Notification/Manager.php | 80 +++++++++++++++++------ lib/private/Support/Subscription/Registry.php | 23 +++---- lib/private/User/Manager.php | 7 +- lib/public/Notification/IManager.php | 12 ++++ lib/public/Support/Subscription/IRegistry.php | 4 +- tests/lib/Notification/ManagerTest.php | 86 +++++++++++++++++++++++-- tests/lib/Support/Subscription/RegistryTest.php | 9 ++- 8 files changed, 180 insertions(+), 42 deletions(-) diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index a55b6a2dac1..9ec176a4ae9 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -4253,6 +4253,7 @@ + !($notification instanceof INotification) !($notification instanceof INotification) diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index fb3a46d5f5d..4e0992053f2 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -27,8 +27,10 @@ declare(strict_types=1); namespace OC\Notification; use OC\AppFramework\Bootstrap\Coordinator; -use OCP\AppFramework\QueryException; -use OCP\ILogger; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IUserManager; use OCP\Notification\AlreadyProcessedException; use OCP\Notification\IApp; use OCP\Notification\IDeferrableApp; @@ -37,11 +39,22 @@ use OCP\Notification\IManager; use OCP\Notification\INotification; use OCP\Notification\INotifier; use OCP\RichObjectStrings\IValidator; +use OCP\Support\Subscription\IRegistry; +use Psr\Container\ContainerExceptionInterface; +use Psr\Log\LoggerInterface; class Manager implements IManager { /** @var IValidator */ protected $validator; - /** @var ILogger */ + /** @var IUserManager */ + private $userManager; + /** @var ICache */ + protected $cache; + /** @var ITimeFactory */ + protected $timeFactory; + /** @var IRegistry */ + protected $subscription; + /** @var LoggerInterface */ protected $logger; /** @var Coordinator */ private $coordinator; @@ -64,9 +77,17 @@ class Manager implements IManager { private $parsedRegistrationContext; public function __construct(IValidator $validator, - ILogger $logger, + IUserManager $userManager, + ICacheFactory $cacheFactory, + ITimeFactory $timeFactory, + IRegistry $subscription, + LoggerInterface $logger, Coordinator $coordinator) { $this->validator = $validator; + $this->userManager = $userManager; + $this->cache = $cacheFactory->createDistributed('notifications'); + $this->timeFactory = $timeFactory; + $this->subscription = $subscription; $this->logger = $logger; $this->coordinator = $coordinator; @@ -97,9 +118,10 @@ class Manager implements IManager { */ public function registerNotifier(\Closure $service, \Closure $info) { $infoData = $info(); - $this->logger->logException(new \InvalidArgumentException( + $exception = new \InvalidArgumentException( 'Notifier ' . $infoData['name'] . ' (id: ' . $infoData['id'] . ') is not considered because it is using the old way to register.' - )); + ); + $this->logger->error($exception->getMessage(), ['exception' => $exception]); } /** @@ -121,10 +143,10 @@ class Manager implements IManager { foreach ($this->appClasses as $appClass) { try { - $app = \OC::$server->query($appClass); - } catch (QueryException $e) { - $this->logger->logException($e, [ - 'message' => 'Failed to load notification app class: ' . $appClass, + $app = \OC::$server->get($appClass); + } catch (ContainerExceptionInterface $e) { + $this->logger->error('Failed to load notification app class: ' . $appClass, [ + 'exception' => $e, 'app' => 'notifications', ]); continue; @@ -153,10 +175,10 @@ class Manager implements IManager { $notifierServices = $this->coordinator->getRegistrationContext()->getNotifierServices(); foreach ($notifierServices as $notifierService) { try { - $notifier = \OC::$server->query($notifierService->getService()); - } catch (QueryException $e) { - $this->logger->logException($e, [ - 'message' => 'Failed to load notification notifier class: ' . $notifierService->getService(), + $notifier = \OC::$server->get($notifierService->getService()); + } catch (ContainerExceptionInterface $e) { + $this->logger->error('Failed to load notification notifier class: ' . $notifierService->getService(), [ + 'exception' => $e, 'app' => 'notifications', ]); continue; @@ -181,10 +203,10 @@ class Manager implements IManager { foreach ($this->notifierClasses as $notifierClass) { try { - $notifier = \OC::$server->query($notifierClass); - } catch (QueryException $e) { - $this->logger->logException($e, [ - 'message' => 'Failed to load notification notifier class: ' . $notifierClass, + $notifier = \OC::$server->get($notifierClass); + } catch (ContainerExceptionInterface $e) { + $this->logger->error('Failed to load notification notifier class: ' . $notifierClass, [ + 'exception' => $e, 'app' => 'notifications', ]); continue; @@ -277,6 +299,28 @@ class Manager implements IManager { $this->deferPushing = false; } + /** + * {@inheritDoc} + */ + public function isFairUseOfFreePushService(): bool { + $pushAllowed = $this->cache->get('push_fair_use'); + if ($pushAllowed === null) { + /** + * 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. + */ + // TODO Remove time check after 1st March 2022 + $isFairUse = $this->timeFactory->getTime() < 1646089200 + || $this->subscription->delegateHasValidSubscription() + || $this->userManager->countSeenUsers() < 5000; + $pushAllowed = $isFairUse ? 'yes' : 'no'; + $this->cache->set('push_fair_use', $pushAllowed, 3600); + } + return $pushAllowed === 'yes'; + } + /** * @param INotification $notification * @throws \InvalidArgumentException When the notification is not valid diff --git a/lib/private/Support/Subscription/Registry.php b/lib/private/Support/Subscription/Registry.php index e64eaac1fa2..1298337acb2 100644 --- a/lib/private/Support/Subscription/Registry.php +++ b/lib/private/Support/Subscription/Registry.php @@ -59,21 +59,17 @@ class Registry implements IRegistry { private $groupManager; /** @var LoggerInterface */ private $logger; - /** @var IManager */ - private $notificationManager; public function __construct(IConfig $config, IServerContainer $container, IUserManager $userManager, IGroupManager $groupManager, - LoggerInterface $logger, - IManager $notificationManager) { + LoggerInterface $logger) { $this->config = $config; $this->container = $container; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->logger = $logger; - $this->notificationManager = $notificationManager; } private function getSubscription(): ?ISubscription { @@ -158,15 +154,16 @@ class Registry implements IRegistry { /** * Indicates if a hard user limit is reached and no new users should be created * + * @param IManager|null $notificationManager * @since 21.0.0 */ - public function delegateIsHardUserLimitReached(): bool { + public function delegateIsHardUserLimitReached(?IManager $notificationManager = null): bool { $subscription = $this->getSubscription(); if ($subscription instanceof ISubscription && $subscription->hasValidSubscription()) { $userLimitReached = $subscription->isHardUserLimitReached(); - if ($userLimitReached) { - $this->notifyAboutReachedUserLimit(); + if ($userLimitReached && $notificationManager instanceof IManager) { + $this->notifyAboutReachedUserLimit($notificationManager); } return $userLimitReached; } @@ -181,8 +178,8 @@ class Registry implements IRegistry { $hardUserLimit = $this->config->getSystemValue('one-click-instance.user-limit', 50); $userLimitReached = $userCount >= $hardUserLimit; - if ($userLimitReached) { - $this->notifyAboutReachedUserLimit(); + if ($userLimitReached && $notificationManager instanceof IManager) { + $this->notifyAboutReachedUserLimit($notificationManager); } return $userLimitReached; } @@ -216,17 +213,17 @@ class Registry implements IRegistry { return $userCount; } - private function notifyAboutReachedUserLimit() { + private function notifyAboutReachedUserLimit(IManager $notificationManager) { $admins = $this->groupManager->get('admin')->getUsers(); foreach ($admins as $admin) { - $notification = $this->notificationManager->createNotification(); + $notification = $notificationManager->createNotification(); $notification->setApp('core') ->setUser($admin->getUID()) ->setDateTime(new \DateTime()) ->setObject('user_limit_reached', '1') ->setSubject('user_limit_reached'); - $this->notificationManager->notify($notification); + $notificationManager->notify($notification); } $this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']); diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index dbbfc2b53a2..7ed80bc5bc2 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -44,6 +44,7 @@ use OCP\IGroup; use OCP\IUser; use OCP\IUserBackend; use OCP\IUserManager; +use OCP\Notification\IManager; use OCP\Support\Subscription\IRegistry; use OCP\User\Backend\IGetRealUIDBackend; use OCP\User\Backend\ISearchKnownUsersBackend; @@ -379,7 +380,11 @@ class Manager extends PublicEmitter implements IUserManager { */ public function createUser($uid, $password) { // DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency - if (\OC::$server->get(IRegistry::class)->delegateIsHardUserLimitReached()) { + /** @var IRegistry $registry */ + $registry = \OC::$server->get(IRegistry::class); + /** @var IManager $notificationManager */ + $notificationManager = \OC::$server->get(IManager::class); + if ($registry->delegateIsHardUserLimitReached($notificationManager)) { $l = \OC::$server->getL10N('lib'); throw new HintException($l->t('The user limit has been reached and the user was not created.')); } diff --git a/lib/public/Notification/IManager.php b/lib/public/Notification/IManager.php index 66fe78b723e..e2f37176850 100644 --- a/lib/public/Notification/IManager.php +++ b/lib/public/Notification/IManager.php @@ -107,4 +107,16 @@ interface IManager extends IApp, INotifier { * @since 20.0.0 */ public function flush(): void; + + /** + * Whether the server can use the hosted push notification service + * + * 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. + * + * @since 23.0.0 + */ + public function isFairUseOfFreePushService(): bool; } diff --git a/lib/public/Support/Subscription/IRegistry.php b/lib/public/Support/Subscription/IRegistry.php index 1082f12ab58..4a34cc91c5e 100644 --- a/lib/public/Support/Subscription/IRegistry.php +++ b/lib/public/Support/Subscription/IRegistry.php @@ -27,6 +27,7 @@ declare(strict_types=1); */ namespace OCP\Support\Subscription; +use OCP\Notification\IManager; use OCP\Support\Subscription\Exception\AlreadyRegisteredException; /** @@ -81,7 +82,8 @@ interface IRegistry { /** * Indicates if a hard user limit is reached and no new users should be created * + * @param IManager|null $notificationManager * @since 21.0.0 */ - public function delegateIsHardUserLimitReached(): bool; + public function delegateIsHardUserLimitReached(?IManager $notificationManager = null): bool; } 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(-) 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 From e55ceb2bb1c22105cd46533f8b41157c52e5da2d Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Wed, 20 Oct 2021 15:24:45 -0300 Subject: Show warning on admin settings page Signed-off-by: Vitor Mattos --- apps/settings/lib/Controller/CheckSetupController.php | 16 +++++++++++++++- core/js/setupchecks.js | 6 ++++++ core/js/tests/specs/setupchecksSpec.js | 19 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php index e18dfb6028b..99e731b594c 100644 --- a/apps/settings/lib/Controller/CheckSetupController.php +++ b/apps/settings/lib/Controller/CheckSetupController.php @@ -78,6 +78,7 @@ use OCP\IRequest; use OCP\ITempManager; use OCP\IURLGenerator; use OCP\Lock\ILockingProvider; +use OCP\Notification\IManager; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -114,6 +115,8 @@ class CheckSetupController extends Controller { private $connection; /** @var ITempManager */ private $tempManager; + /** @var IManager */ + private $manager; public function __construct($AppName, IRequest $request, @@ -131,7 +134,8 @@ class CheckSetupController extends Controller { ISecureRandom $secureRandom, IniGetWrapper $iniGetWrapper, IDBConnection $connection, - ITempManager $tempManager) { + ITempManager $tempManager, + IManager $manager) { parent::__construct($AppName, $request); $this->config = $config; $this->clientService = $clientService; @@ -148,6 +152,15 @@ class CheckSetupController extends Controller { $this->iniGetWrapper = $iniGetWrapper; $this->connection = $connection; $this->tempManager = $tempManager; + $this->manager = $manager; + } + + /** + * Check if is fair use of free push service + * @return bool + */ + private function isFairUseOfFreePushService(): bool { + return $this->manager->isFairUseOfFreePushService(); } /** @@ -761,6 +774,7 @@ Raw output 'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(), 'cronInfo' => $this->getLastCronInfo(), 'cronErrors' => $this->getCronErrors(), + 'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(), 'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(), 'isMemcacheConfigured' => $this->isMemcacheConfigured(), 'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'), diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index a16f50e122a..f5f48fdf384 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -254,6 +254,12 @@ type: OC.SetupChecks.MESSAGE_TYPE_ERROR }); } + if (!data.isFairUseOfFreePushService) { + messages.push({ + msg: t('core', 'This is the unsupported community build of Nextcloud. Given the size of this instance, performance, reliability and scalability cannot be guaranteed. Push notifications have been disabled to avoid overloading our free service. Learn more about the benefits of Nextcloud Enterprise at nextcloud.com/enterprise.'), + type: OC.SetupChecks.MESSAGE_TYPE_ERROR + }); + } if (data.serverHasInternetConnectionProblems) { messages.push({ msg: t('core', 'This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features.'), diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js index 82f9c26224c..451c49fb2e9 100644 --- a/core/js/tests/specs/setupchecksSpec.js +++ b/core/js/tests/specs/setupchecksSpec.js @@ -230,6 +230,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: true, memcacheDocs: 'https://docs.nextcloud.com/server/go.php?to=admin-performance', forwardedForHeadersWorking: true, @@ -287,6 +288,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: true, memcacheDocs: 'https://docs.nextcloud.com/server/go.php?to=admin-performance', forwardedForHeadersWorking: true, @@ -345,6 +347,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: true, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -401,6 +404,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: false, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -455,6 +459,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -509,6 +514,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -564,6 +570,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: false, @@ -618,6 +625,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -672,6 +680,7 @@ describe('OC.SetupChecks tests', function() { hasValidTransactionIsolationLevel: true, suggestedOverwriteCliURL: '', isRandomnessSecure: true, + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -747,6 +756,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -802,6 +812,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -857,6 +868,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -912,6 +924,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -967,6 +980,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -1025,6 +1039,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -1080,6 +1095,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -1132,6 +1148,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -1186,6 +1203,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, @@ -1240,6 +1258,7 @@ describe('OC.SetupChecks tests', function() { suggestedOverwriteCliURL: '', isRandomnessSecure: true, securityDocs: 'https://docs.nextcloud.com/myDocs.html', + isFairUseOfFreePushService: true, serverHasInternetConnectionProblems: false, isMemcacheConfigured: true, forwardedForHeadersWorking: true, -- cgit v1.2.3 From 6f7ca3432c0e6d1e1925179e952f79c6273295f3 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Thu, 21 Oct 2021 13:24:53 -0300 Subject: show warning on personal settings page Signed-off-by: Vitor Mattos --- apps/settings/lib/Settings/Personal/PersonalInfo.php | 17 ++++++++++++++++- .../templates/settings/personal/personal.info.php | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/settings/lib/Settings/Personal/PersonalInfo.php b/apps/settings/lib/Settings/Personal/PersonalInfo.php index 928c18998df..72c443ed1b6 100644 --- a/apps/settings/lib/Settings/Personal/PersonalInfo.php +++ b/apps/settings/lib/Settings/Personal/PersonalInfo.php @@ -52,6 +52,7 @@ use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; use OC\Profile\ProfileManager; +use OCP\Notification\IManager; use OCP\Settings\ISettings; class PersonalInfo implements ISettings { @@ -84,6 +85,9 @@ class PersonalInfo implements ISettings { /** @var IInitialState */ private $initialStateService; + /** @var IManager */ + private $manager; + public function __construct( IConfig $config, IUserManager $userManager, @@ -93,7 +97,8 @@ class PersonalInfo implements ISettings { IAppManager $appManager, IFactory $l10nFactory, IL10N $l, - IInitialState $initialStateService + IInitialState $initialStateService, + IManager $manager ) { $this->config = $config; $this->userManager = $userManager; @@ -104,6 +109,7 @@ class PersonalInfo implements ISettings { $this->l10nFactory = $l10nFactory; $this->l = $l; $this->initialStateService = $initialStateService; + $this->manager = $manager; } public function getForm(): TemplateResponse { @@ -160,6 +166,7 @@ class PersonalInfo implements ISettings { 'twitterScope' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(), 'twitterVerification' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getVerified(), 'groups' => $this->getGroups($user), + 'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService() ] + $messageParameters + $languageParameters + $localeParameters; $personalInfoParameters = [ @@ -190,6 +197,14 @@ class PersonalInfo implements ISettings { return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, ''); } + /** + * Check if is fair use of free push service + * @return boolean + */ + private function isFairUseOfFreePushService(): bool { + return $this->manager->isFairUseOfFreePushService(); + } + /** * returns the primary biography in an * associative array diff --git a/apps/settings/templates/settings/personal/personal.info.php b/apps/settings/templates/settings/personal/personal.info.php index d258f616229..4cd0e4e34d6 100644 --- a/apps/settings/templates/settings/personal/personal.info.php +++ b/apps/settings/templates/settings/personal/personal.info.php @@ -35,6 +35,13 @@ script('settings', [ 'vue-settings-personal-info', ]); ?> + +
+
+ t('This community release of Nextcloud is unsupported and instant notifications are unavailable.')); ?> +
+
+
-- cgit v1.2.3