From 79a0ee4f4ae315b357f51dd35d3d3c6e60897ee1 Mon Sep 17 00:00:00 2001
From: Christoph Wurst <christoph@winzerhof-wurst.at>
Date: Wed, 26 Sep 2018 16:25:18 +0200
Subject: [PATCH] Consolidate personal two-factor provider settings

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
---
 apps/twofactor_backupcodes/js/settings.js     | Bin 108962 -> 108980 bytes
 apps/twofactor_backupcodes/js/settings.js.map | Bin 542141 -> 542181 bytes
 .../lib/Provider/BackupCodesProvider.php      |  15 ++++-
 .../lib/Settings/Personal.php                 |  63 +++---------------
 .../src/views/PersonalSettings.vue            |   6 +-
 .../templates/personal.php                    |   5 +-
 lib/private/Settings/Personal/Security.php    |  52 +++++++++++++--
 .../IPersonalProviderSettings.php             |  43 ++++++++++++
 .../IProvidesPersonalSettings.php             |  47 +++++++++++++
 settings/css/settings.scss                    |  20 ++++++
 .../templates/settings/personal/security.php  |  35 ++++++++++
 11 files changed, 220 insertions(+), 66 deletions(-)
 create mode 100644 lib/public/Authentication/TwoFactorAuth/IPersonalProviderSettings.php
 create mode 100644 lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php

diff --git a/apps/twofactor_backupcodes/js/settings.js b/apps/twofactor_backupcodes/js/settings.js
index 0341dbf13a6bbccef68520adb9ed01448f462eeb..29252548de595cd6659c7844ed241a01134bbad7 100644
GIT binary patch
delta 43
zcmZ2<nQhBuwuUW?F0(ll3W_pw6N@UhyUb=>%*2|fp_H0C{mER$p6z+_7)`hVf_D(S

delta 42
zcmV+_0M-Ar(*~l`27t5yM4Puvn*pN&2xTr`b|@lTZkJ)40V<a%o&i_4f}Q~~3u$`~
AZvX%Q

diff --git a/apps/twofactor_backupcodes/js/settings.js.map b/apps/twofactor_backupcodes/js/settings.js.map
index 01fc5d81ea47cceed6b01dfae336d77be013bbd8..27b3318ff64a2ea40823d2de7e764ee97ffad3f2 100644
GIT binary patch
delta 315
zcmdnnt@yNCaRYP0W}XB##%7tsc9}%R?J|i>Q>2(OowjeRX3}HgaMW>ibad45oqng5
z$(1?Pv1+<X9aAY|>GWB3Owx=6+qcy*^|7#dIy!nA7H<!1W|CoI1#v>R*R(PzGjmls
z>j1f)8AVRh*LN_<aaUA1>3BOj=QtHQx8Lnx0%B$$W&vVWAZFWsw}X9VKZim=QD$yp
zQRQ?;X%5-xX9YO8w$GK|*ul=0tP!J>nmhf2EQjdy`{Ep2(>I842v5&d;82?$AkU#O
z{gxz$<n(h297fasDR3lDj~4-Q^~8WguOiSOAXj+$PDKvB?b{VORxt5qCFbeprB;+A
v=j4~BOuy*BA+vpk3P+#--2Uw^O*mAUm~DVo@|trrFsJ6)ZSOYcFyaFMEC^>I

delta 362
zcmYj}O-lk%6o&DR>5=@Hth7YMsg^@lWYY4^7$q$V`T;uVgyDpf<D?YXLPAR@TEwA<
za@8s*h`1nxf1_oa_Wp!sYSZF5=jAys=esL>bcI(g?=f;kbyqR9+8dX5IXvRbp{eFn
zW4{GT8x#ivh1^6OJM8h8%oF@PArhz12F9Aij2&!HF-x;_r6h@Kl+wB`i>kFnLy|T2
z(j=EopY1h0W8XI$Om2}V3H*1<dWIFx$SlX3W%>7uJ+iAt2Cq9L&Kp~Qa-GVWbd;3Z
z{SL9<ESwf27B1HBxNooUf`>8eMZu5HJOIkf9mL@Tcv)`>?%X&CgBx=RyCZ-Xn+b^E
zb`+fWkbr4B#c>#fCG-iPpdNuq91TI(F2`^nK!AM-@I=N7rRtthtLUX<y;3b3x>8Wt
Z`5e@Fyib9jg;EeExR{1h_MV0X{|~EWas>bY

diff --git a/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php b/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php
index 03d1ee0d405..27653c1dd61 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 eb28dacb42b..0b71b1da088 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 ac53de2e65c..fb4980abb59 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 e440158f4cd..4c887aff915 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 efcfd5589ce..0efe2d0746b 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 00000000000..80552d1fe8c
--- /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 00000000000..5ba7ad547e9
--- /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 ae1a456b699..08baaed4cc6 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 6dd0e8d3cef..6b2c37b6a88 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>
-- 
GitLab