From ed920c439c5934a1a5e728e3bcda42da3848f4fa Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 6 Nov 2020 16:30:11 +0100 Subject: [PATCH] Facelift adding TOTP devices --- .../templates/__totpNewDeviceContainer.tpl | 44 +++++++++++++++++++ .../templates/__totpSecretField.tpl | 2 + .../Core/Ui/User/Multifactor/Totp/Qr.js | 4 +- .../TotpMultifactorMethod.class.php | 14 +++--- .../totp/NewDeviceContainer.class.php | 29 ++++++++++++ .../files/style/ui/accountSecurity.scss | 19 ++++++++ .../Core/Ui/User/Multifactor/Totp/Qr.ts | 4 +- 7 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 com.woltlab.wcf/templates/__totpNewDeviceContainer.tpl create mode 100644 wcfsetup/install/files/lib/system/user/multifactor/totp/NewDeviceContainer.class.php diff --git a/com.woltlab.wcf/templates/__totpNewDeviceContainer.tpl b/com.woltlab.wcf/templates/__totpNewDeviceContainer.tpl new file mode 100644 index 0000000000..f2c9db9312 --- /dev/null +++ b/com.woltlab.wcf/templates/__totpNewDeviceContainer.tpl @@ -0,0 +1,44 @@ +
getClasses()|empty} class="{implode from=$container->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* + *}{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* + *}{if !$container->checkDependencies()} style="display: none;"{/if}{* +*}> + {if $container->getLabel() !== null} + {if $container->getDescription() !== null} +
+

{@$container->getLabel()}{if $container->markAsRequired()} *{/if}

+

{@$container->getDescription()}

+
+ {else} +

{@$container->getLabel()}{if $container->markAsRequired()} *{/if}

+ {/if} + {/if} + +
+ {if $container->getNodeById('secret')->isAvailable()} + {@$container->getNodeById('secret')->getHtml()} + {/if} + +
+ {foreach from=$container item='child'} + {if $child->getId() !== 'secret' && $child->getId() !== 'submitButton' && $child->isAvailable()} + {@$child->getHtml()} + {/if} + {/foreach} + + {if $container->getNodeById('submitButton')->isAvailable()} +
+ {@$container->getNodeById('submitButton')->getHtml()} +
+ {/if} +
+
+
+ +{include file='__formContainerDependencies'} + + diff --git a/com.woltlab.wcf/templates/__totpSecretField.tpl b/com.woltlab.wcf/templates/__totpSecretField.tpl index 023f7ecda1..eb3fe6d0c5 100644 --- a/com.woltlab.wcf/templates/__totpSecretField.tpl +++ b/com.woltlab.wcf/templates/__totpSecretField.tpl @@ -1,5 +1,7 @@
+ + addDefaultButton(false); + $newDeviceContainer = NewDeviceContainer::create() ->label('wcf.user.security.multifactor.totp.newDevice') ->appendChildren([ SecretFormField::create(), @@ -69,6 +68,11 @@ class TotpMultifactorMethod implements IMultifactorMethod { TextFormField::create('deviceName') ->label('wcf.user.security.multifactor.totp.deviceName') ->placeholder('wcf.user.security.multifactor.totp.deviceName.placeholder'), + FormButton::create('submitButton') + ->label('wcf.global.button.submit') + ->accessKey('s') + ->submit(true) + ->addClass('buttonPrimary'), ]); $form->appendChild($newDeviceContainer); } diff --git a/wcfsetup/install/files/lib/system/user/multifactor/totp/NewDeviceContainer.class.php b/wcfsetup/install/files/lib/system/user/multifactor/totp/NewDeviceContainer.class.php new file mode 100644 index 0000000000..b3cf109a02 --- /dev/null +++ b/wcfsetup/install/files/lib/system/user/multifactor/totp/NewDeviceContainer.class.php @@ -0,0 +1,29 @@ + + * @package WoltLabSuite\System\User\Multifactor\Totp + * @since 5.4 + */ +class NewDeviceContainer extends FormContainer { + use TDefaultIdFormField; + + /** + * @inheritDoc + */ + protected $templateName = '__totpNewDeviceContainer'; + + /** + * @inheritDoc + */ + protected static function getDefaultId(): string { + return 'newDevice'; + } +} diff --git a/wcfsetup/install/files/style/ui/accountSecurity.scss b/wcfsetup/install/files/style/ui/accountSecurity.scss index 9cf57e45ee..476a0804e0 100644 --- a/wcfsetup/install/files/style/ui/accountSecurity.scss +++ b/wcfsetup/install/files/style/ui/accountSecurity.scss @@ -20,3 +20,22 @@ } } } + +.multifactorTotpNewDevice { + display: flex; + + .totpSecretContainer { + text-align: center; + width: 250px; + margin: 0 5px; + + canvas { + width: 200px; + height: 200px; + } + } + + .multifactorTotpNewDeviceFields { + flex: 1 1 auto; + } +} diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/User/Multifactor/Totp/Qr.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/User/Multifactor/Totp/Qr.ts index e9af381a4d..f00dd0fb08 100644 --- a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/User/Multifactor/Totp/Qr.ts +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/User/Multifactor/Totp/Qr.ts @@ -14,13 +14,15 @@ export function render(container: HTMLElement): void { const issuer = secret.dataset.issuer; const label = (issuer ? `${issuer}:` : "") + accountName; + const canvas = container.querySelector("canvas"); QrCreator.render( { text: `otpauth://totp/${encodeURIComponent(label)}?secret=${encodeURIComponent(secret.textContent!)}${ issuer ? `&issuer=${encodeURIComponent(issuer)}` : "" }`, + size: canvas && canvas.clientWidth ? canvas.clientWidth : 200, }, - container, + canvas || container, ); } -- 2.20.1