diff --git a/apps/settings/appinfo/routes.php b/apps/settings/appinfo/routes.php index e238510b1a75f..6b7cc634fc208 100644 --- a/apps/settings/appinfo/routes.php +++ b/apps/settings/appinfo/routes.php @@ -36,6 +36,7 @@ ['name' => 'AuthorizedGroup#destroy', 'url' => '/settings/authorizedgroups', 'verb' => 'DELETE'], ['name' => 'AuthSettings#wipe', 'url' => '/settings/personal/authtokens/wipe/{id}', 'verb' => 'POST' , 'root' => ''], + ['name' => 'AccountWarnings#getAll', 'url' => '/settings/accountwarnings', 'verb' => 'GET' , 'root' => ''], ['name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST' , 'root' => ''], ['name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST' , 'root' => ''], @@ -84,5 +85,8 @@ ['name' => 'WebAuthn#deleteRegistration', 'url' => '/settings/api/personal/webauthn/registration/{id}', 'verb' => 'DELETE' , 'root' => ''], ['name' => 'Reasons#getPdf', 'url' => '/settings/download/reasons', 'verb' => 'GET', 'root' => ''], - ] + ], + 'ocs' => [ + ['name' => 'ClientDiagnostics#update', 'url' => '/diagnostics', 'verb' => 'PUT' ], + ], ]; diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 674b92b7cac36..dd0d4bf671179 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -7,6 +7,13 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'OCA\\Settings\\AccountWarnings\\AccountWarningsManager' => $baseDir . '/../lib/AccountWarnings/AccountWarningsManager.php', + 'OCA\\Settings\\AccountWarnings\\ClientDiagnosticWarning' => $baseDir . '/../lib/AccountWarnings/ClientDiagnosticWarning.php', + 'OCA\\Settings\\AccountWarnings\\ClientDiagnosticWarningsProvider' => $baseDir . '/../lib/AccountWarnings/ClientDiagnosticWarningsProvider.php', + 'OCA\\Settings\\AccountWarnings\\OutdatedClientWarning' => $baseDir . '/../lib/AccountWarnings/OutdatedClientWarning.php', + 'OCA\\Settings\\AccountWarnings\\OutdatedClientWarningsProvider' => $baseDir . '/../lib/AccountWarnings/OutdatedClientWarningsProvider.php', + 'OCA\\Settings\\AccountWarnings\\QuotaWarning' => $baseDir . '/../lib/AccountWarnings/QuotaWarning.php', + 'OCA\\Settings\\AccountWarnings\\QuotaWarningsProvider' => $baseDir . '/../lib/AccountWarnings/QuotaWarningsProvider.php', 'OCA\\Settings\\Activity\\GroupProvider' => $baseDir . '/../lib/Activity/GroupProvider.php', 'OCA\\Settings\\Activity\\GroupSetting' => $baseDir . '/../lib/Activity/GroupSetting.php', 'OCA\\Settings\\Activity\\Provider' => $baseDir . '/../lib/Activity/Provider.php', @@ -17,12 +24,14 @@ 'OCA\\Settings\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => $baseDir . '/../lib/BackgroundJobs/VerifyUserData.php', 'OCA\\Settings\\Controller\\AISettingsController' => $baseDir . '/../lib/Controller/AISettingsController.php', + 'OCA\\Settings\\Controller\\AccountWarningsController' => $baseDir . '/../lib/Controller/AccountWarningsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => $baseDir . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => $baseDir . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => $baseDir . '/../lib/Controller/AuthSettingsController.php', 'OCA\\Settings\\Controller\\AuthorizedGroupController' => $baseDir . '/../lib/Controller/AuthorizedGroupController.php', 'OCA\\Settings\\Controller\\ChangePasswordController' => $baseDir . '/../lib/Controller/ChangePasswordController.php', 'OCA\\Settings\\Controller\\CheckSetupController' => $baseDir . '/../lib/Controller/CheckSetupController.php', + 'OCA\\Settings\\Controller\\ClientDiagnosticsController' => $baseDir . '/../lib/Controller/ClientDiagnosticsController.php', 'OCA\\Settings\\Controller\\CommonSettingsTrait' => $baseDir . '/../lib/Controller/CommonSettingsTrait.php', 'OCA\\Settings\\Controller\\HelpController' => $baseDir . '/../lib/Controller/HelpController.php', 'OCA\\Settings\\Controller\\LogSettingsController' => $baseDir . '/../lib/Controller/LogSettingsController.php', @@ -32,6 +41,8 @@ 'OCA\\Settings\\Controller\\TwoFactorSettingsController' => $baseDir . '/../lib/Controller/TwoFactorSettingsController.php', 'OCA\\Settings\\Controller\\UsersController' => $baseDir . '/../lib/Controller/UsersController.php', 'OCA\\Settings\\Controller\\WebAuthnController' => $baseDir . '/../lib/Controller/WebAuthnController.php', + 'OCA\\Settings\\Db\\ClientDiagnostic' => $baseDir . '/../lib/Db/ClientDiagnostic.php', + 'OCA\\Settings\\Db\\ClientDiagnosticMapper' => $baseDir . '/../lib/Db/ClientDiagnosticMapper.php', 'OCA\\Settings\\Events\\BeforeTemplateRenderedEvent' => $baseDir . '/../lib/Events/BeforeTemplateRenderedEvent.php', 'OCA\\Settings\\Hooks' => $baseDir . '/../lib/Hooks.php', 'OCA\\Settings\\Listener\\AppPasswordCreatedActivityListener' => $baseDir . '/../lib/Listener/AppPasswordCreatedActivityListener.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index d78aa56c60642..5485e54549a66 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -22,6 +22,13 @@ class ComposerStaticInitSettings public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'OCA\\Settings\\AccountWarnings\\AccountWarningsManager' => __DIR__ . '/..' . '/../lib/AccountWarnings/AccountWarningsManager.php', + 'OCA\\Settings\\AccountWarnings\\ClientDiagnosticWarning' => __DIR__ . '/..' . '/../lib/AccountWarnings/ClientDiagnosticWarning.php', + 'OCA\\Settings\\AccountWarnings\\ClientDiagnosticWarningsProvider' => __DIR__ . '/..' . '/../lib/AccountWarnings/ClientDiagnosticWarningsProvider.php', + 'OCA\\Settings\\AccountWarnings\\OutdatedClientWarning' => __DIR__ . '/..' . '/../lib/AccountWarnings/OutdatedClientWarning.php', + 'OCA\\Settings\\AccountWarnings\\OutdatedClientWarningsProvider' => __DIR__ . '/..' . '/../lib/AccountWarnings/OutdatedClientWarningsProvider.php', + 'OCA\\Settings\\AccountWarnings\\QuotaWarning' => __DIR__ . '/..' . '/../lib/AccountWarnings/QuotaWarning.php', + 'OCA\\Settings\\AccountWarnings\\QuotaWarningsProvider' => __DIR__ . '/..' . '/../lib/AccountWarnings/QuotaWarningsProvider.php', 'OCA\\Settings\\Activity\\GroupProvider' => __DIR__ . '/..' . '/../lib/Activity/GroupProvider.php', 'OCA\\Settings\\Activity\\GroupSetting' => __DIR__ . '/..' . '/../lib/Activity/GroupSetting.php', 'OCA\\Settings\\Activity\\Provider' => __DIR__ . '/..' . '/../lib/Activity/Provider.php', @@ -32,12 +39,14 @@ class ComposerStaticInitSettings 'OCA\\Settings\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => __DIR__ . '/..' . '/../lib/BackgroundJobs/VerifyUserData.php', 'OCA\\Settings\\Controller\\AISettingsController' => __DIR__ . '/..' . '/../lib/Controller/AISettingsController.php', + 'OCA\\Settings\\Controller\\AccountWarningsController' => __DIR__ . '/..' . '/../lib/Controller/AccountWarningsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AuthSettingsController.php', 'OCA\\Settings\\Controller\\AuthorizedGroupController' => __DIR__ . '/..' . '/../lib/Controller/AuthorizedGroupController.php', 'OCA\\Settings\\Controller\\ChangePasswordController' => __DIR__ . '/..' . '/../lib/Controller/ChangePasswordController.php', 'OCA\\Settings\\Controller\\CheckSetupController' => __DIR__ . '/..' . '/../lib/Controller/CheckSetupController.php', + 'OCA\\Settings\\Controller\\ClientDiagnosticsController' => __DIR__ . '/..' . '/../lib/Controller/ClientDiagnosticsController.php', 'OCA\\Settings\\Controller\\CommonSettingsTrait' => __DIR__ . '/..' . '/../lib/Controller/CommonSettingsTrait.php', 'OCA\\Settings\\Controller\\HelpController' => __DIR__ . '/..' . '/../lib/Controller/HelpController.php', 'OCA\\Settings\\Controller\\LogSettingsController' => __DIR__ . '/..' . '/../lib/Controller/LogSettingsController.php', @@ -47,6 +56,8 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Controller\\TwoFactorSettingsController' => __DIR__ . '/..' . '/../lib/Controller/TwoFactorSettingsController.php', 'OCA\\Settings\\Controller\\UsersController' => __DIR__ . '/..' . '/../lib/Controller/UsersController.php', 'OCA\\Settings\\Controller\\WebAuthnController' => __DIR__ . '/..' . '/../lib/Controller/WebAuthnController.php', + 'OCA\\Settings\\Db\\ClientDiagnostic' => __DIR__ . '/..' . '/../lib/Db/ClientDiagnostic.php', + 'OCA\\Settings\\Db\\ClientDiagnosticMapper' => __DIR__ . '/..' . '/../lib/Db/ClientDiagnosticMapper.php', 'OCA\\Settings\\Events\\BeforeTemplateRenderedEvent' => __DIR__ . '/..' . '/../lib/Events/BeforeTemplateRenderedEvent.php', 'OCA\\Settings\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php', 'OCA\\Settings\\Listener\\AppPasswordCreatedActivityListener' => __DIR__ . '/..' . '/../lib/Listener/AppPasswordCreatedActivityListener.php', diff --git a/apps/settings/lib/AccountWarnings/AccountWarningsManager.php b/apps/settings/lib/AccountWarnings/AccountWarningsManager.php new file mode 100644 index 0000000000000..70f61f7251c06 --- /dev/null +++ b/apps/settings/lib/AccountWarnings/AccountWarningsManager.php @@ -0,0 +1,58 @@ + + * + * @author Côme Chilliet + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Settings\AccountWarnings; + +use OCP\Settings\IAccountWarningsProvider; +use OC\AppFramework\Bootstrap\Coordinator; + +class AccountWarningsManager { + public function __construct( + private Coordinator $coordinator, + ) { + } + + /** + * @return array}> + */ + public function getAll(): array { + $results = []; + $providerRegistrations = $this->coordinator->getRegistrationContext()->getAccountWarningsProviders(); + foreach ($providerRegistrations as $providerRegistration) { + /** @var IAccountWarningsProvider $provider */ + $provider = \OCP\Server::get($providerRegistration->getService()); + $warnings = $provider->getAccountWarnings(); + $results[$provider::class] = ['name' => $provider->getName(),'warnings' => []]; + foreach ($warnings as $warning) { + $category = $warning->getSeverity(); + if (!isset($results[$provider::class]['warnings'][$category])) { + $results[$provider::class]['warnings'][$category] = []; + } + $results[$provider::class]['warnings'][$category][] = $warning->getText(); + } + } + return $results; + } +} diff --git a/apps/settings/lib/AccountWarnings/ClientDiagnosticWarning.php b/apps/settings/lib/AccountWarnings/ClientDiagnosticWarning.php new file mode 100644 index 0000000000000..4cd10e267ffc2 --- /dev/null +++ b/apps/settings/lib/AccountWarnings/ClientDiagnosticWarning.php @@ -0,0 +1,77 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\AccountWarnings; + +use OCA\Settings\Db\ClientDiagnostic; +use OCP\Settings\IAccountWarning; +use OCP\IL10N; + +class ClientDiagnosticWarning implements IAccountWarning { + /** + * @param string $type one of ClientDiagnostic::TYPE_* constants + */ + public function __construct( + private IL10N $l10n, + private string $type, + private int $count, + private string $uid, + private string $clientName, + private int $oldest, + ) { + } + + public function getText(): string { + $oldest = new \DateTime(); + $oldest->setTimestamp($this->oldest); + // TODO check which format to use + $formattedOldest = $oldest->format('Y-m-d H:i:s'); + return match ($this->type) { + ClientDiagnostic::TYPE_CONFLICT => + $this->l10n->n( + 'Account "%s" had %n conflict on client %s on %s', + 'Account "%s" had %n conflicts on client %s, oldest one on %s', + $this->count, + [$this->uid, $this->clientName, $formattedOldest] + ), + ClientDiagnostic::TYPE_FAILED_UPLOAD => + $this->l10n->n( + 'Account %s had %n failed upload on client %s on %s', + 'Account %s had %n failed uploads on client %s, oldest one on %s', + $this->count, + [$this->uid, $this->clientName, $formattedOldest] + ), + default => 'Unknown problem', + }; + } + + public function getSeverity(): string { + return match ($this->type) { + ClientDiagnostic::TYPE_CONFLICT => IAccountWarning::SEVERITY_WARNING, + ClientDiagnostic::TYPE_FAILED_UPLOAD => IAccountWarning::SEVERITY_WARNING, + default => IAccountWarning::SEVERITY_ERROR, + }; + } +} diff --git a/apps/settings/lib/AccountWarnings/ClientDiagnosticWarningsProvider.php b/apps/settings/lib/AccountWarnings/ClientDiagnosticWarningsProvider.php new file mode 100644 index 0000000000000..c895600736bcb --- /dev/null +++ b/apps/settings/lib/AccountWarnings/ClientDiagnosticWarningsProvider.php @@ -0,0 +1,88 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Settings\AccountWarnings; + +use OCA\Settings\Db\ClientDiagnostic; +use OCA\Settings\Db\ClientDiagnosticMapper; +use OCP\Settings\IAccountWarningsProvider; +use OC\Authentication\Token\IToken; +use OC\Authentication\Token\IProvider; +use OCP\IL10N; +use OCP\IUserManager; + +class ClientDiagnosticWarningsProvider implements IAccountWarningsProvider { + /** + * Maximum age for a client diagnostic to be considered still valid, in seconds + * Diagnostic timestamp will be compared to last check of the associated authtoken + */ + public const MAX_AGE = 24 * 60 * 60; + + public function __construct( + private IL10N $l10n, + private IUserManager $userManager, + private ClientDiagnosticMapper $diagnosticMapper, + private IProvider $tokenProvider, + ) { + } + + public function getName(): string { + return $this->l10n->t('Client errors'); + } + + public function getAccountWarnings(): array { + $diagnostics = $this->diagnosticMapper->getAll(); + + $warnings = []; + foreach ($diagnostics as $diagnostic) { + $token = $this->getAuthtoken($diagnostic); + // if (!$this->isRecentEnough($diagnostic, $token)) { + // // TODO delete diagnostic? + // continue; + // } + $data = $diagnostic->getDiagnosticAsArray(); + foreach ($data['problems'] as $type => $problemDetails) { + $warnings[] = new ClientDiagnosticWarning( + $this->l10n, + $type, + $problemDetails['count'], + $token->getUID(), + $token->getName(), + $problemDetails['oldest'] + ); + } + } + return $warnings; + } + + private function getAuthtoken(ClientDiagnostic $diagnostic): IToken { + return $this->tokenProvider->getTokenById($diagnostic->getAuthtokenid()); + } + + private function isRecentEnough(ClientDiagnostic $diagnostic, IToken $token): bool { + return ($diagnostic->getTimestamp()->getTimestamp() >= $token->getLastCheck() + self::MAX_AGE); + } +} diff --git a/apps/settings/lib/AccountWarnings/OutdatedClientWarning.php b/apps/settings/lib/AccountWarnings/OutdatedClientWarning.php new file mode 100644 index 0000000000000..726eee1c9cd94 --- /dev/null +++ b/apps/settings/lib/AccountWarnings/OutdatedClientWarning.php @@ -0,0 +1,46 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\AccountWarnings; + +use OCP\Settings\IAccountWarning; +use OCP\IL10N; + +class OutdatedClientWarning implements IAccountWarning { + public function __construct( + private IL10N $l10n, + private int $count, + private string $version, + ) { + } + + public function getText(): string { + return $this->l10n->t('%d users are using outdated client version "%s"', [$this->count, $this->version]); + } + + public function getSeverity(): string { + return IAccountWarning::SEVERITY_WARNING; + } +} diff --git a/apps/settings/lib/AccountWarnings/OutdatedClientWarningsProvider.php b/apps/settings/lib/AccountWarnings/OutdatedClientWarningsProvider.php new file mode 100644 index 0000000000000..f5d9f0198847e --- /dev/null +++ b/apps/settings/lib/AccountWarnings/OutdatedClientWarningsProvider.php @@ -0,0 +1,44 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\AccountWarnings; + +use OCP\Settings\IAccountWarningsProvider; +use OCP\IL10N; + +class OutdatedClientWarningsProvider implements IAccountWarningsProvider { + public function __construct( + private IL10N $l10n, + ) { + } + + public function getName(): string { + return $this->l10n->t('Outdated clients'); + } + + public function getAccountWarnings(): array { + return [new OutdatedClientWarning($this->l10n, 2, 'Example Desktop client 3.5.0')]; + } +} diff --git a/apps/settings/lib/AccountWarnings/QuotaWarning.php b/apps/settings/lib/AccountWarnings/QuotaWarning.php new file mode 100644 index 0000000000000..8bd40b1e26741 --- /dev/null +++ b/apps/settings/lib/AccountWarnings/QuotaWarning.php @@ -0,0 +1,57 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\AccountWarnings; + +use OCP\Settings\IAccountWarning; +use OCP\IL10N; + +class QuotaWarning implements IAccountWarning { + public const THRESHOLDS = [ + 98 => IAccountWarning::SEVERITY_ERROR, + 90 => IAccountWarning::SEVERITY_WARNING, + 80 => IAccountWarning::SEVERITY_INFO, + ]; + + public function __construct( + private IL10N $l10n, + private array $userIds, + private int $threshold, + ) { + } + + public function getText(): string { + return $this->l10n->n( + '%n account is using more than %d%% of their quota: %s', + '%n accounts are using more than %d%% of their quota: %s', + count($this->userIds), + [$this->threshold, implode(',', $this->userIds)] + ); + } + + public function getSeverity(): string { + return self::THRESHOLDS[$this->threshold]; + } +} diff --git a/apps/settings/lib/AccountWarnings/QuotaWarningsProvider.php b/apps/settings/lib/AccountWarnings/QuotaWarningsProvider.php new file mode 100644 index 0000000000000..518ed0f16cbdf --- /dev/null +++ b/apps/settings/lib/AccountWarnings/QuotaWarningsProvider.php @@ -0,0 +1,76 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Settings\AccountWarnings; + +use OCP\Settings\IAccountWarningsProvider; +use OCP\IL10N; +use OCP\IUserManager; +use OCP\IUser; +use OCP\Files\IRootFolder; + +class QuotaWarningsProvider implements IAccountWarningsProvider { + public function __construct( + private IL10N $l10n, + private IUserManager $userManager, + private IRootFolder $rootFolder, + ) { + } + + public function getName(): string { + return $this->l10n->t('Quota'); + } + + public function getAccountWarnings(): array { + $users = []; + foreach (QuotaWarning::THRESHOLDS as $threshold) { + $users[$threshold] = []; + } + $this->userManager->callForSeenUsers(function (IUser $user) use (&$users) { + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $size = $userFolder->getSize(); + $quota = \OCP\Util::computerFileSize($user->getQuota()); + if ($quota === false) { + return; + } + $usage = 100 * ($size / $quota); + foreach (QuotaWarning::THRESHOLDS as $threshold => $level) { + if ($usage >= $threshold) { + $users[$threshold][] = $user->getUID(); + break; + } + } + }); + $warnings = []; + foreach ($users as $threshold => $uids) { + $count = count($uids); + if ($count > 0) { + $warnings[] = new QuotaWarning($this->l10n, $uids, $threshold); + } + } + return $warnings; + } +} diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 8d99f7f4f8687..320a7e245d28e 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -37,6 +37,9 @@ use OC\Authentication\Events\AppPasswordCreatedEvent; use OC\Authentication\Token\IProvider; use OC\Server; +use OCA\Settings\AccountWarnings\ClientDiagnosticWarningsProvider; +use OCA\Settings\AccountWarnings\OutdatedClientWarningsProvider; +use OCA\Settings\AccountWarnings\QuotaWarningsProvider; use OCA\Settings\Hooks; use OCA\Settings\Listener\AppPasswordCreatedActivityListener; use OCA\Settings\Listener\GroupRemovedListener; @@ -136,6 +139,10 @@ public function register(IRegistrationContext $context): void { }); $context->registerUserMigrator(AccountMigrator::class); + + $context->registerAccountWarningsProvider(ClientDiagnosticWarningsProvider::class); + $context->registerAccountWarningsProvider(OutdatedClientWarningsProvider::class); + $context->registerAccountWarningsProvider(QuotaWarningsProvider::class); } public function boot(IBootContext $context): void { diff --git a/apps/settings/lib/Controller/AccountWarningsController.php b/apps/settings/lib/Controller/AccountWarningsController.php new file mode 100644 index 0000000000000..8b76a6a06e85b --- /dev/null +++ b/apps/settings/lib/Controller/AccountWarningsController.php @@ -0,0 +1,45 @@ + + * + * @author Côme Chilliet + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Settings\Controller; + +use OCA\Settings\AccountWarnings\AccountWarningsManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; +use OCP\IRequest; + +class AccountWarningsController extends Controller { + public function __construct( + string $appName, + IRequest $request, + private AccountWarningsManager $manager, + ) { + parent::__construct($appName, $request); + } + + public function getAll(): DataResponse { + return new DataResponse($this->manager->getAll()); + } +} diff --git a/apps/settings/lib/Controller/ClientDiagnosticsController.php b/apps/settings/lib/Controller/ClientDiagnosticsController.php new file mode 100644 index 0000000000000..76b797bd5a1c7 --- /dev/null +++ b/apps/settings/lib/Controller/ClientDiagnosticsController.php @@ -0,0 +1,96 @@ + + * + * @author Côme Chilliet + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Settings\Controller; + +use OCA\Settings\Db\ClientDiagnostic; +use OCA\Settings\Db\ClientDiagnosticMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\IRequest; +use OCP\ISession; +use OCP\IUserSession; +use OCP\Session\Exceptions\SessionNotAvailableException; +use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Token\IProvider; +use Psr\Clock\ClockInterface; + +class ClientDiagnosticsController extends OCSController { + public function __construct( + string $appName, + IRequest $request, + private ISession $session, + private IUserSession $userSession, + private ClientDiagnosticMapper $mapper, + private IProvider $tokenProvider, + private ClockInterface $clock, + ) { + parent::__construct($appName, $request); + } + + /** + * @NoAdminRequired + * @NoSubAdminRequired + * @NoCSRFRequired + */ + public function update(array $problems): DataResponse { + try { + $sessionId = $this->session->getId(); + } catch (SessionNotAvailableException $e) { + return new DataResponse(['message' => $e->getMessage()], Http::STATUS_SERVICE_UNAVAILABLE); + } + if ($this->userSession->getImpersonatingUserID() !== null) { + return new DataResponse([], Http::STATUS_METHOD_NOT_ALLOWED); + } + + $appPassword = $this->session->get('app_password'); + + try { + $token = $this->tokenProvider->getToken($appPassword); + } catch (InvalidTokenException $e) { + return new DataResponse([], Http::STATUS_METHOD_NOT_ALLOWED); + } + + /* TODO: validate problems structure */ + + try { + $entity = $this->mapper->findByAuthtokenid($token->getId()); + $entity->setDiagnostic(json_encode(['problems' => $problems], JSON_THROW_ON_ERROR)); + $entity->setTimestamp(\DateTime::createFromImmutable($this->clock->now())); + $this->mapper->update($entity); + } catch (DoesNotExistException $e) { + $entity = $this->mapper->insert( + ClientDiagnostic::fromParams([ + 'authtokenid' => $token->getId(), + 'diagnostic' => json_encode(['problems' => $problems], JSON_THROW_ON_ERROR), + 'timestamp' => \DateTime::createFromImmutable($this->clock->now()), + ])); + } + + return new DataResponse([]); + } +} diff --git a/apps/settings/lib/Db/ClientDiagnostic.php b/apps/settings/lib/Db/ClientDiagnostic.php new file mode 100644 index 0000000000000..339522b678958 --- /dev/null +++ b/apps/settings/lib/Db/ClientDiagnostic.php @@ -0,0 +1,66 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Settings\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method void setAuthtokenid(int $authtokenid) + * @method int getAuthtokenid() + * @method void setDiagnostic(string $diagnostic) + * @method string getDiagnostic() + * @method \DateTime getTimestamp() + * @method void setTimestamp(\DateTime $timestamp) + */ +class ClientDiagnostic extends Entity { + public const TYPE_CONFLICT = 'conflict'; + public const TYPE_FAILED_UPLOAD = 'failed-upload'; + public const TYPES = [ + self::TYPE_CONFLICT, + self::TYPE_FAILED_UPLOAD, + ]; + + /** @var int */ + protected $authtokenid; + + /** @var string json-encoded*/ + protected $diagnostic; + + /** @var \DateTime */ + public $timestamp; + + public function __construct() { + $this->addType('authtokenid', 'int'); + $this->addType('diagnostic', 'string'); + $this->addType('timestamp', 'datetime'); + } + + public function getDiagnosticAsArray(): array { + $data = json_decode($this->diagnostic, true, JSON_THROW_ON_ERROR); + return $data; + } +} diff --git a/apps/settings/lib/Db/ClientDiagnosticMapper.php b/apps/settings/lib/Db/ClientDiagnosticMapper.php new file mode 100644 index 0000000000000..2d881804af63d --- /dev/null +++ b/apps/settings/lib/Db/ClientDiagnosticMapper.php @@ -0,0 +1,69 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Settings\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\QBMapper; +use OCP\IDBConnection; + +/** + * @template-extends QBMapper + */ +class ClientDiagnosticMapper extends QBMapper { + public const TABLE_NAME = 'client_diagnostics'; + + public function __construct(IDBConnection $db) { + parent::__construct($db, self::TABLE_NAME, ClientDiagnostic::class); + } + + /** + * @return ClientDiagnostic[] + */ + public function getAll(): array { + $qb = $this->db->getQueryBuilder(); + + $select = $qb + ->select('*') + ->from($this->getTableName()); + + return $this->findEntities($select); + } + + /** + * @throws DoesNotExistException + */ + public function findByAuthtokenid(int $id): ClientDiagnostic { + $qb = $this->db->getQueryBuilder(); + + $select = $qb + ->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('authtokenid', $qb->createNamedParameter($id, $qb::PARAM_INT))); + + return $this->findEntity($select); + } +} diff --git a/core/Migrations/Version28000Date20231002171502.php b/core/Migrations/Version28000Date20231002171502.php new file mode 100644 index 0000000000000..70860730d9dd6 --- /dev/null +++ b/core/Migrations/Version28000Date20231002171502.php @@ -0,0 +1,75 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCA\Settings\Db\ClientDiagnosticMapper; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Introduce client_diagnostics table + */ +class Version28000Date20231002171502 extends SimpleMigrationStep { + /** + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable(ClientDiagnosticMapper::TABLE_NAME)) { + $table = $schema->createTable(ClientDiagnosticMapper::TABLE_NAME); + + $table->addColumn('id', Types::BIGINT, [ + 'notnull' => true, + 'length' => 64, + 'autoincrement' => true, + ]); + $table->addColumn('authtokenid', Types::BIGINT, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('diagnostic', Types::TEXT, [ + 'notnull' => true, + ]); + $table->addColumn('timestamp', Types::DATETIME, [ + 'notnull' => true, + ]); + + $table->setPrimaryKey(['id'], 'client_diag_id_primary'); + $table->addUniqueIndex(['authtokenid'], 'client_diag_authtokenid_index'); + + $changed = true; + return $schema; + } + + return null; + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 6e5bf85efcf93..d3082c2b5a446 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -595,6 +595,8 @@ 'OCP\\Security\\VerificationToken\\InvalidTokenException' => $baseDir . '/lib/public/Security/VerificationToken/InvalidTokenException.php', 'OCP\\Server' => $baseDir . '/lib/public/Server.php', 'OCP\\Session\\Exceptions\\SessionNotAvailableException' => $baseDir . '/lib/public/Session/Exceptions/SessionNotAvailableException.php', + 'OCP\\Settings\\IAccountWarning' => $baseDir . '/lib/public/Settings/IAccountWarning.php', + 'OCP\\Settings\\IAccountWarningsProvider' => $baseDir . '/lib/public/Settings/IAccountWarningsProvider.php', 'OCP\\Settings\\IDelegatedSettings' => $baseDir . '/lib/public/Settings/IDelegatedSettings.php', 'OCP\\Settings\\IIconSection' => $baseDir . '/lib/public/Settings/IIconSection.php', 'OCP\\Settings\\IManager' => $baseDir . '/lib/public/Settings/IManager.php', @@ -1174,6 +1176,7 @@ 'OC\\Core\\Migrations\\Version28000Date20230616104802' => $baseDir . '/core/Migrations/Version28000Date20230616104802.php', 'OC\\Core\\Migrations\\Version28000Date20230728104802' => $baseDir . '/core/Migrations/Version28000Date20230728104802.php', 'OC\\Core\\Migrations\\Version28000Date20230803221055' => $baseDir . '/core/Migrations/Version28000Date20230803221055.php', + 'OC\\Core\\Migrations\\Version28000Date20231002171502' => $baseDir . '/core/Migrations/Version28000Date20231002171502.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index ef88bcd2a9b19..6d763a2689485 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -628,6 +628,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Security\\VerificationToken\\InvalidTokenException' => __DIR__ . '/../../..' . '/lib/public/Security/VerificationToken/InvalidTokenException.php', 'OCP\\Server' => __DIR__ . '/../../..' . '/lib/public/Server.php', 'OCP\\Session\\Exceptions\\SessionNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/Session/Exceptions/SessionNotAvailableException.php', + 'OCP\\Settings\\IAccountWarning' => __DIR__ . '/../../..' . '/lib/public/Settings/IAccountWarning.php', + 'OCP\\Settings\\IAccountWarningsProvider' => __DIR__ . '/../../..' . '/lib/public/Settings/IAccountWarningsProvider.php', 'OCP\\Settings\\IDelegatedSettings' => __DIR__ . '/../../..' . '/lib/public/Settings/IDelegatedSettings.php', 'OCP\\Settings\\IIconSection' => __DIR__ . '/../../..' . '/lib/public/Settings/IIconSection.php', 'OCP\\Settings\\IManager' => __DIR__ . '/../../..' . '/lib/public/Settings/IManager.php', @@ -1207,6 +1209,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version28000Date20230616104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230616104802.php', 'OC\\Core\\Migrations\\Version28000Date20230728104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230728104802.php', 'OC\\Core\\Migrations\\Version28000Date20230803221055' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230803221055.php', + 'OC\\Core\\Migrations\\Version28000Date20231002171502' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20231002171502.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 5aea2a7a744b0..ed137ac958cad 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -55,6 +55,7 @@ use OCP\Notification\INotifier; use OCP\Profile\ILinkAction; use OCP\Search\IProvider; +use OCP\Settings\IAccountWarningsProvider; use OCP\Share\IPublicShareTemplateProvider; use OCP\Support\CrashReport\IReporter; use OCP\UserMigration\IMigrator as IUserMigrator; @@ -146,6 +147,9 @@ class RegistrationContext { /** @var ServiceRegistration[] */ private $publicShareTemplateProviders = []; + /** @var ServiceRegistration[] */ + private array $accountWarningsProviders = []; + /** @var LoggerInterface */ private $logger; @@ -372,6 +376,13 @@ public function registerPublicShareTemplateProvider(string $class): void { $class ); } + + public function registerAccountWarningsProvider(string $class): void { + $this->context->registerAccountWarningsProvider( + $this->appId, + $class + ); + } }; } @@ -523,6 +534,10 @@ public function registerPublicShareTemplateProvider(string $appId, string $class $this->publicShareTemplateProviders[] = new ServiceRegistration($appId, $class); } + public function registerAccountWarningsProvider(string $appId, string $class): void { + $this->accountWarningsProviders[] = new ServiceRegistration($appId, $class); + } + /** * @param App[] $apps */ @@ -828,4 +843,11 @@ public function getSensitiveMethods(): array { public function getPublicShareTemplateProviders(): array { return $this->publicShareTemplateProviders; } + + /** + * @return ServiceRegistration[] + */ + public function getAccountWarningsProviders(): array { + return $this->accountWarningsProviders; + } } diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 720803a78d170..b4f252daf43b8 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -371,4 +371,14 @@ public function registerSensitiveMethods(string $class, array $methods): void; * @since 26.0.0 */ public function registerPublicShareTemplateProvider(string $class): void; + + /** + * Register an implementation of IAccountWarningsProvider. + * + * @param string $class + * @psalm-param class-string<\OCP\Settings\IAccountWarningsProvider> $class + * @return void + * @since 28.0.0 + */ + public function registerAccountWarningsProvider(string $class): void; } diff --git a/lib/public/Settings/IAccountWarning.php b/lib/public/Settings/IAccountWarning.php new file mode 100644 index 0000000000000..d23dc70e0dbfa --- /dev/null +++ b/lib/public/Settings/IAccountWarning.php @@ -0,0 +1,49 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Settings; + +/** + * @since 28.0.0 + */ +interface IAccountWarning { + public const SEVERITY_INFO = 'info'; + public const SEVERITY_WARNING = 'warning'; + public const SEVERITY_ERROR = 'error'; + + /** + * Text to show to the admin + * @since 28.0.0 + */ + public function getText(): string; + + /** + * Severity, one the SEVERITY_* constants + * @return self::SEVERITY_INFO|self::SEVERITY_WARNING|self::SEVERITY_ERROR + * @since 28.0.0 + */ + public function getSeverity(): string; +} diff --git a/lib/public/Settings/IAccountWarningsProvider.php b/lib/public/Settings/IAccountWarningsProvider.php new file mode 100644 index 0000000000000..67a12e2146568 --- /dev/null +++ b/lib/public/Settings/IAccountWarningsProvider.php @@ -0,0 +1,44 @@ + + * + * @author Côme Chilliet + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Settings; + +/** + * @since 28.0.0 + */ +interface IAccountWarningsProvider { + /** + * @return IAccountWarning[] Warnings detected by this provider + * @since 28.0.0 + */ + public function getAccountWarnings(): array; + + /** + * Name to show as tab name in the UI + * @since 28.0.0 + */ + public function getName(): string; +}