Merge pull request #29363 from nextcloud/fair-use-push

This commit is contained in:
John Molakvoæ 2021-10-23 10:53:22 +02:00 committed by GitHub
commit a06001e085
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 285 additions and 52 deletions

View file

@ -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'),

View file

@ -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

View file

@ -35,6 +35,13 @@ script('settings', [
'vue-settings-personal-info',
]);
?>
<?php if (!$_['isFairUseOfFreePushService']) : ?>
<div class="section">
<div class="warning">
<?php p($l->t('This community release of Nextcloud is unsupported and instant notifications are unavailable.')); ?>
</div>
</div>
<?php endif; ?>
<div id="personal-settings" data-federation-enabled="<?php p($_['federationEnabled'] ? 'true' : 'false') ?>"
data-lookup-server-upload-enabled="<?php p($_['lookupServerUploadEnabled'] ? 'true' : 'false') ?>">

View file

@ -4253,6 +4253,7 @@
</ParamNameMismatch>
</file>
<file src="lib/private/Notification/Manager.php">
<InvalidCatch occurrences="3"/>
<TypeDoesNotContainType occurrences="2">
<code>!($notification instanceof INotification)</code>
<code>!($notification instanceof INotification)</code>

View file

@ -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);

View file

@ -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.'),

View file

@ -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,

View file

@ -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

View file

@ -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']);

View file

@ -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.'));
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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',

View file

@ -1,4 +1,6 @@
<?php
declare(strict_types=1);
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
@ -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());
}
}

View file

@ -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));
}
}