diff --git a/apps/twofactor_backupcodes/js/settings.js b/apps/twofactor_backupcodes/js/settings.js index 0341dbf13a6bbccef68520adb9ed01448f462eeb..29252548de595cd6659c7844ed241a01134bbad7 100644 Binary files a/apps/twofactor_backupcodes/js/settings.js and b/apps/twofactor_backupcodes/js/settings.js differ diff --git a/apps/twofactor_backupcodes/js/settings.js.map b/apps/twofactor_backupcodes/js/settings.js.map index 01fc5d81ea47cceed6b01dfae336d77be013bbd8..27b3318ff64a2ea40823d2de7e764ee97ffad3f2 100644 Binary files a/apps/twofactor_backupcodes/js/settings.js.map and b/apps/twofactor_backupcodes/js/settings.js.map differ diff --git a/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php b/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php index 03d1ee0d40572a9f042f0afac15a7d9ab178753f..27653c1dd619db50a212dccfa9ad69d7dd1f74c4 100644 --- a/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php +++ b/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php @@ -25,12 +25,15 @@ namespace OCA\TwoFactorBackupCodes\Provider; use OC\App\AppManager; use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; +use OCA\TwoFactorBackupCodes\Settings\Personal; +use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; use OCP\Authentication\TwoFactorAuth\IProvider; +use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings; use OCP\IL10N; use OCP\IUser; use OCP\Template; -class BackupCodesProvider implements IProvider { +class BackupCodesProvider implements IProvider, IProvidesPersonalSettings { /** @var string */ private $appName; @@ -139,4 +142,14 @@ class BackupCodesProvider implements IProvider { return false; } + /** + * @param IUser $user + * + * @return IPersonalProviderSettings + */ + public function getPersonalSettings(IUser $user): IPersonalProviderSettings { + return new Personal(); + } + } + diff --git a/apps/twofactor_backupcodes/lib/Settings/Personal.php b/apps/twofactor_backupcodes/lib/Settings/Personal.php index eb28dacb42b5edbc663133c764d4e055302a5baf..0b71b1da0886e1fdcc2529efac5f5e6b8fe12dca 100644 --- a/apps/twofactor_backupcodes/lib/Settings/Personal.php +++ b/apps/twofactor_backupcodes/lib/Settings/Personal.php @@ -1,8 +1,9 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * @author Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * @@ -24,59 +25,13 @@ namespace OCA\TwoFactorBackupCodes\Settings; -use OCA\TwoFactorBackupCodes\AppInfo\Application; -use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\IUserSession; -use OCP\Settings\ISettings; - -class Personal implements ISettings { - - /** @var Application */ - private $app; - /** @var BackupCodesProvider */ - private $provider; - /** @var IUserSession */ - private $userSession; +use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; +use OCP\Template; - public function __construct(Application $app, BackupCodesProvider $provider, IUserSession $userSession) { - $this->app = $app; - $this->provider = $provider; - $this->userSession = $userSession; - } - - /** - * @return TemplateResponse returns the instance with all parameters set, ready to be rendered - * @since 9.1 - */ - public function getForm() { - $templateOwner = 'settings'; - $templateName = 'settings/empty'; - if ($this->provider->isActive($this->userSession->getUser())) { - $templateOwner = $this->app->getContainer()->getAppName(); - $templateName = 'personal'; - } +class Personal implements IPersonalProviderSettings { - return new TemplateResponse($templateOwner, $templateName, [], ''); + public function getBody(): Template { + return new Template('twofactor_backupcodes', 'personal'); } - /** - * @return string the section ID, e.g. 'sharing' - * @since 9.1 - */ - public function getSection() { - return 'security'; - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. - * - * E.g.: 70 - * @since 9.1 - */ - public function getPriority() { - return 40; - } } diff --git a/apps/twofactor_backupcodes/src/views/PersonalSettings.vue b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue index ac53de2e65cf74d0dc75de33a26bdc73245edaf8..fb4980abb59ea731f0a340a3bf7e1804334cf1d3 100644 --- a/apps/twofactor_backupcodes/src/views/PersonalSettings.vue +++ b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue @@ -14,7 +14,7 @@ <li v-for="code in codes" class="backup-code">{{code}}</li> </ul> <a :href="downloadUrl" - class="button" + class="button primary" download="Nextcloud-backup-codes.txt">{{ t('twofactor_backupcodes', 'Save backup codes') }}</a> <button class="button" v-on:click="printCodes">{{ t('twofactor_backupcodes', 'Print backup codes') }}</button> @@ -25,9 +25,9 @@ :class="{'icon-loading-small': generatingCodes}" v-on:click="generateBackupCodes">{{ t('twofactor_backupcodes', 'Regenerate backup codes') }}</button> </p> - <p> + <p><em> {{ t('twofactor_backupcodes', 'If you regenerate backup codes, you automatically invalidate old codes.') }} - </p> + </em></p> </template> </div> </template> diff --git a/apps/twofactor_backupcodes/templates/personal.php b/apps/twofactor_backupcodes/templates/personal.php index e440158f4cdf554538e8b17cc4a454666d9d195a..4c887aff91596b3bfe487f4619e1bcff58348018 100644 --- a/apps/twofactor_backupcodes/templates/personal.php +++ b/apps/twofactor_backupcodes/templates/personal.php @@ -5,7 +5,4 @@ style('twofactor_backupcodes', 'style'); ?> -<div class="section"> - <h2 data-anchor-name="second-factor-backup-codes"><?php p($l->t('Second-factor backup codes')); ?></h2> - <div id="twofactor-backupcodes-settings"></div> -</div> +<div id="twofactor-backupcodes-settings"></div> diff --git a/lib/private/Settings/Personal/Security.php b/lib/private/Settings/Personal/Security.php index efcfd5589ceb74fea38f0cd6b34324a514d34fd6..0efe2d0746b3dc0b0d019d7fe06578e1d8c76a2c 100644 --- a/lib/private/Settings/Personal/Security.php +++ b/lib/private/Settings/Personal/Security.php @@ -24,18 +24,41 @@ namespace OC\Settings\Personal; +use function array_filter; +use function array_map; +use function is_null; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorManager; +use OC\Authentication\TwoFactorAuth\ProviderLoader; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Authentication\TwoFactorAuth\IProvider; +use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings; use OCP\IUserManager; +use OCP\IUserSession; use OCP\Settings\ISettings; class Security implements ISettings { + /** @var IUserManager */ private $userManager; - public function __construct( - IUserManager $userManager - ) { + /** @var TwoFactorManager */ + private $twoFactorManager; + + /** @var ProviderLoader */ + private $providerLoader; + + /** @var IUserSession */ + private $userSession; + + + public function __construct(IUserManager $userManager, + TwoFactorManager $providerManager, + ProviderLoader $providerLoader, + IUserSession $userSession) { $this->userManager = $userManager; + $this->twoFactorManager = $providerManager; + $this->providerLoader = $providerLoader; + $this->userSession = $userSession; } /** @@ -50,7 +73,8 @@ class Security implements ISettings { } return new TemplateResponse('settings', 'settings/personal/security', [ - 'passwordChangeSupported' => $passwordChangeSupported + 'passwordChangeSupported' => $passwordChangeSupported, + 'twoFactorProviderData' => $this->getTwoFactorProviderData(), ]); } @@ -73,4 +97,24 @@ class Security implements ISettings { public function getPriority() { return 10; } + + private function getTwoFactorProviderData(): array { + $user = $this->userSession->getUser(); + if (is_null($user)) { + // Actually impossible, but still … + return []; + } + + return [ + 'isEnabled' => $this->twoFactorManager->isTwoFactorAuthenticated($user), + 'providers' => array_map(function (IProvidesPersonalSettings $provider) use ($user) { + return [ + 'provider' => $provider, + 'settings' => $provider->getPersonalSettings($user) + ]; + }, array_filter($this->providerLoader->getProviders($user), function (IProvider $provider) { + return $provider instanceof IProvidesPersonalSettings; + })) + ]; + } } diff --git a/lib/public/Authentication/TwoFactorAuth/IPersonalProviderSettings.php b/lib/public/Authentication/TwoFactorAuth/IPersonalProviderSettings.php new file mode 100644 index 0000000000000000000000000000000000000000..80552d1fe8cf1a6610bcde9772d26bcc8e449727 --- /dev/null +++ b/lib/public/Authentication/TwoFactorAuth/IPersonalProviderSettings.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Authentication\TwoFactorAuth; + +use OCP\Template; + +/** + * Interface IPersonalProviderSettings + * + * @since 15.0.0 + */ +interface IPersonalProviderSettings { + + /** + * @return Template + * + * @since 15.0.0 + */ + public function getBody(): Template; + +} diff --git a/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php b/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php new file mode 100644 index 0000000000000000000000000000000000000000..5ba7ad547e976194d4909ffd340724656a6d4ca3 --- /dev/null +++ b/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Authentication\TwoFactorAuth; + +use OCP\IUser; + +/** + * Interface for admins that have personal settings. These settings will be shown in the + * security sections. Some information like the display name of the provider is read + * from the provider directly. + * + * @since 15.0.0 + */ +interface IProvidesPersonalSettings extends IProvider { + + /** + * @param IUser $user + * + * @return IPersonalProviderSettings + * + * @since 15.0.0 + */ + public function getPersonalSettings(IUser $user): IPersonalProviderSettings; + +} diff --git a/settings/css/settings.scss b/settings/css/settings.scss index ae1a456b699fd231a142b3bd5ae11b33f4358a08..08baaed4cc6877afccb4f287f23660d3c943da38 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -471,6 +471,26 @@ table.nostyle { } } + + +/* Two-Factor Authentication (2FA) */ + +#two-factor-auth { + h3 { + margin-top: 24px; + } + + li > div { + margin-left: 20px; + } + + .two-factor-provider-settings-icon { + width: 16px; + height: 16px; + } +} + + #new-app-login-name, #new-app-password { width: 245px; diff --git a/settings/templates/settings/personal/security.php b/settings/templates/settings/personal/security.php index 6dd0e8d3cefea175fa7b5de5f2c712c1e34fc816..6b2c37b6a88968bb35a16863c9d111413a26f8e9 100644 --- a/settings/templates/settings/personal/security.php +++ b/settings/templates/settings/personal/security.php @@ -101,3 +101,38 @@ if($_['passwordChangeSupported']) { </div> </div> </div> + +<div id="two-factor-auth" class="section"> + <h2><?php p($l->t('Two-Factor Authentication'));?></h2> + <p class="settings-hint"> + <?php + if ($_['twoFactorProviderData']['enabled']) { + p($l->t('Two-factor authentication is enabled on your account.')); + } else { + p($l->t('Two-factor authentication is disabled on your account.')); + } + ?> + </p> + <ul> + <?php foreach ($_['twoFactorProviderData']['providers'] as $data) { ?> + <li> + <?php + /** @var \OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings $provider */ + $provider = $data['provider']; + if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { + $icon = $provider->getDarkIcon(); + } else { + $icon = image_path('core', 'actions/password.svg'); + } + /** @var \OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings $settings */ + $settings = $data['settings']; + ?> + <h3> + <img class="two-factor-provider-settings-icon" src="<?php p($icon) ?>" alt=""> + <?php p($provider->getDisplayName()) ?> + </h3> + <?php print_unescaped($settings->getBody()->fetchPage()) ?> + </li> + <?php } ?> + </ul> +</div>