Signed-off-by: Morris Jobke <hey@morrisjobke.de>tags/v21.0.0beta1
@@ -5098,8 +5098,9 @@ | |||
</UndefinedInterfaceMethod> | |||
</file> | |||
<file src="lib/private/Support/Subscription/Registry.php"> | |||
<UndefinedInterfaceMethod occurrences="1"> | |||
<UndefinedInterfaceMethod occurrences="2"> | |||
<code>getSupportedApps</code> | |||
<code>countUsers</code> | |||
</UndefinedInterfaceMethod> | |||
</file> | |||
<file src="lib/private/SystemTag/SystemTagManager.php"> |
@@ -28,13 +28,16 @@ declare(strict_types=1); | |||
namespace OC\Support\Subscription; | |||
use OC\User\Backend; | |||
use OCP\AppFramework\QueryException; | |||
use OCP\IConfig; | |||
use OCP\IServerContainer; | |||
use OCP\IUserManager; | |||
use OCP\Support\Subscription\Exception\AlreadyRegisteredException; | |||
use OCP\Support\Subscription\IRegistry; | |||
use OCP\Support\Subscription\ISubscription; | |||
use OCP\Support\Subscription\ISupportedApps; | |||
use Psr\Log\LoggerInterface; | |||
class Registry implements IRegistry { | |||
@@ -49,10 +52,19 @@ class Registry implements IRegistry { | |||
/** @var IServerContainer */ | |||
private $container; | |||
public function __construct(IConfig $config, IServerContainer $container) { | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var LoggerInterface */ | |||
private $logger; | |||
public function __construct(IConfig $config, | |||
IServerContainer $container, | |||
IUserManager $userManager, | |||
LoggerInterface $logger) { | |||
$this->config = $config; | |||
$this->container = $container; | |||
$this->userManager = $userManager; | |||
$this->logger = $logger; | |||
} | |||
private function getSubscription(): ?ISubscription { | |||
@@ -127,9 +139,76 @@ class Registry implements IRegistry { | |||
* @since 17.0.0 | |||
*/ | |||
public function delegateHasExtendedSupport(): bool { | |||
if ($this->getSubscription() instanceof ISubscription && method_exists($this->subscription, 'hasExtendedSupport')) { | |||
if ($this->getSubscription() instanceof ISubscription) { | |||
return $this->getSubscription()->hasExtendedSupport(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* Indicates if a hard user limit is reached and no new users should be created | |||
* | |||
* @since 21.0.0 | |||
*/ | |||
public function delegateIsHardUserLimitReached(): bool { | |||
$subscription = $this->getSubscription(); | |||
if ($subscription instanceof ISubscription && | |||
$subscription->hasValidSubscription()) { | |||
$userLimitReached = $subscription->isHardUserLimitReached(); | |||
if ($userLimitReached) { | |||
$this->notifyAboutReachedUserLimit(); | |||
} | |||
return $userLimitReached; | |||
} | |||
$isOneClickInstance = $this->config->getSystemValueBool('one-click-instance', false); | |||
if (!$isOneClickInstance) { | |||
return false; | |||
} | |||
$userCount = $this->getUserCount(); | |||
$hardUserLimit = $this->config->getSystemValue('one-click-instance.user-limit', 50); | |||
$userLimitReached = $userCount >= $hardUserLimit; | |||
if ($userLimitReached) { | |||
$this->notifyAboutReachedUserLimit(); | |||
} | |||
return $userLimitReached; | |||
} | |||
private function getUserCount(): int { | |||
$userCount = 0; | |||
$backends = $this->userManager->getBackends(); | |||
foreach ($backends as $backend) { | |||
if ($backend->implementsActions(Backend::COUNT_USERS)) { | |||
$backendUsers = $backend->countUsers(); | |||
if ($backendUsers !== false) { | |||
$userCount += $backendUsers; | |||
} else { | |||
// TODO what if the user count can't be determined? | |||
$this->logger->warning('Can not determine user count for ' . get_class($backend), ['app' => 'lib']); | |||
} | |||
} | |||
} | |||
$disabledUsers = $this->config->getUsersForUserValue('core', 'enabled', 'false'); | |||
$disabledUsersCount = count($disabledUsers); | |||
$userCount = $userCount - $disabledUsersCount; | |||
if ($userCount < 0) { | |||
$userCount = 0; | |||
// this should never happen | |||
$this->logger->warning("Total user count was negative (users: $userCount, disabled: $disabledUsersCount)", ['app' => 'lib']); | |||
} | |||
return $userCount; | |||
} | |||
private function notifyAboutReachedUserLimit() { | |||
// TODO notify admin about reached user limit | |||
$this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']); | |||
} | |||
} |
@@ -34,6 +34,7 @@ | |||
namespace OC\User; | |||
use OC\HintException; | |||
use OC\Hooks\PublicEmitter; | |||
use OCP\DB\QueryBuilder\IQueryBuilder; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
@@ -42,6 +43,7 @@ use OCP\IGroup; | |||
use OCP\IUser; | |||
use OCP\IUserBackend; | |||
use OCP\IUserManager; | |||
use OCP\Support\Subscription\IRegistry; | |||
use OCP\User\Backend\IGetRealUIDBackend; | |||
use OCP\User\Events\BeforeUserCreatedEvent; | |||
use OCP\User\Events\UserCreatedEvent; | |||
@@ -297,6 +299,12 @@ class Manager extends PublicEmitter implements IUserManager { | |||
* @return bool|IUser the created user or false | |||
*/ | |||
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()) { | |||
$l = \OC::$server->getL10N('lib'); | |||
throw new HintException($l->t('The user limit has been reached and the user was not created.')); | |||
} | |||
$localBackends = []; | |||
foreach ($this->backends as $backend) { | |||
if ($backend instanceof Database) { |
@@ -78,4 +78,11 @@ interface IRegistry { | |||
* @since 17.0.0 | |||
*/ | |||
public function delegateHasExtendedSupport(): bool; | |||
/** | |||
* Indicates if a hard user limit is reached and no new users should be created | |||
* | |||
* @since 21.0.0 | |||
*/ | |||
public function delegateIsHardUserLimitReached(): bool; | |||
} |
@@ -45,4 +45,11 @@ interface ISubscription { | |||
* @since 17.0.0 | |||
*/ | |||
public function hasExtendedSupport(): bool; | |||
/** | |||
* Indicates if a hard user limit is reached and no new users should be created | |||
* | |||
* @since 21.0.0 | |||
*/ | |||
public function isHardUserLimitReached(): bool; | |||
} |
@@ -30,6 +30,8 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription { | |||
private $hasValidSubscription; | |||
/** @var bool */ | |||
private $hasExtendedSupport; | |||
/** @var bool */ | |||
private $isHardUserLimitReached; | |||
/** | |||
* DummySubscription constructor. | |||
@@ -37,9 +39,10 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription { | |||
* @param bool $hasValidSubscription | |||
* @param bool $hasExtendedSupport | |||
*/ | |||
public function __construct(bool $hasValidSubscription, bool $hasExtendedSupport) { | |||
public function __construct(bool $hasValidSubscription, bool $hasExtendedSupport, bool $isHardUserLimitReached) { | |||
$this->hasValidSubscription = $hasValidSubscription; | |||
$this->hasExtendedSupport = $hasExtendedSupport; | |||
$this->isHardUserLimitReached = $isHardUserLimitReached; | |||
} | |||
/** | |||
@@ -55,4 +58,8 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription { | |||
public function hasExtendedSupport(): bool { | |||
return $this->hasExtendedSupport; | |||
} | |||
public function isHardUserLimitReached(): bool { | |||
return $this->isHardUserLimitReached; | |||
} | |||
} |
@@ -25,9 +25,11 @@ namespace Test\Support\Subscription; | |||
use OC\Support\Subscription\Registry; | |||
use OCP\IConfig; | |||
use OCP\IServerContainer; | |||
use OCP\IUserManager; | |||
use OCP\Support\Subscription\ISubscription; | |||
use OCP\Support\Subscription\ISupportedApps; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
use Psr\Log\LoggerInterface; | |||
use Test\TestCase; | |||
class RegistryTest extends TestCase { | |||
@@ -41,12 +43,25 @@ class RegistryTest extends TestCase { | |||
/** @var MockObject|IServerContainer */ | |||
private $serverContainer; | |||
/** @var MockObject|IUserManager */ | |||
private $userManager; | |||
/** @var MockObject|LoggerInterface */ | |||
private $logger; | |||
protected function setUp(): void { | |||
parent::setUp(); | |||
$this->config = $this->createMock(IConfig::class); | |||
$this->serverContainer = $this->createMock(IServerContainer::class); | |||
$this->registry = new Registry($this->config, $this->serverContainer); | |||
$this->userManager = $this->createMock(IUserManager::class); | |||
$this->logger = $this->createMock(LoggerInterface::class); | |||
$this->registry = new Registry( | |||
$this->config, | |||
$this->serverContainer, | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
/** | |||
@@ -121,10 +136,74 @@ class RegistryTest extends TestCase { | |||
public function testSubscriptionService() { | |||
$this->serverContainer->method('query') | |||
->with(DummySubscription::class) | |||
->willReturn(new DummySubscription(true, false)); | |||
->willReturn(new DummySubscription(true, false, false)); | |||
$this->registry->registerService(DummySubscription::class); | |||
$this->assertTrue($this->registry->delegateHasValidSubscription()); | |||
$this->assertFalse($this->registry->delegateHasExtendedSupport()); | |||
} | |||
public function testDelegateIsHardUserLimitReached() { | |||
/* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */ | |||
$subscription = $this->createMock(ISubscription::class); | |||
$subscription->expects($this->once()) | |||
->method('hasValidSubscription') | |||
->willReturn(true); | |||
$subscription->expects($this->once()) | |||
->method('isHardUserLimitReached') | |||
->willReturn(true); | |||
$this->registry->register($subscription); | |||
$this->assertSame(true, $this->registry->delegateIsHardUserLimitReached()); | |||
} | |||
public function testDelegateIsHardUserLimitReachedWithoutSupportApp() { | |||
$this->config->expects($this->once()) | |||
->method('getSystemValueBool') | |||
->with('one-click-instance') | |||
->willReturn(false); | |||
$this->assertSame(false, $this->registry->delegateIsHardUserLimitReached()); | |||
} | |||
public function dataForUserLimitCheck() { | |||
return [ | |||
// $userLimit, $userCount, $disabledUsers, $expectedResult | |||
[35, 15, 2, false], | |||
[35, 45, 15, false], | |||
[35, 45, 5, true], | |||
[35, 45, 55, false], | |||
]; | |||
} | |||
/** | |||
* @dataProvider dataForUserLimitCheck | |||
*/ | |||
public function testDelegateIsHardUserLimitReachedWithoutSupportAppAndUserCount($userLimit, $userCount, $disabledUsers, $expectedResult) { | |||
$this->config->expects($this->once()) | |||
->method('getSystemValueBool') | |||
->with('one-click-instance') | |||
->willReturn(true); | |||
$this->config->expects($this->once()) | |||
->method('getSystemValue') | |||
->with('one-click-instance.user-limit') | |||
->willReturn($userLimit); | |||
$this->config->expects($this->once()) | |||
->method('getUsersForUserValue') | |||
->with('core', 'enabled', 'false') | |||
->willReturn(array_fill(0, $disabledUsers, '')); | |||
/* @var UserInterface|\PHPUnit\Framework\MockObject\MockObject $dummyBackend */ | |||
$dummyBackend = $this->createMock(UserInterface::class); | |||
$dummyBackend->expects($this->once()) | |||
->method('implementsActions') | |||
->willReturn(true); | |||
$dummyBackend->expects($this->once()) | |||
->method('countUsers') | |||
->willReturn($userCount); | |||
$this->userManager->expects($this->once()) | |||
->method('getBackends') | |||
->willReturn([$dummyBackend]); | |||
$this->assertSame($expectedResult, $this->registry->delegateIsHardUserLimitReached()); | |||
} | |||
} |