From 74a5b6e5ec40a2164b49d36b4b8f5ff2156d02f1 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Fri, 9 Aug 2019 11:00:53 +0200 Subject: Add possibility to set up at login Signed-off-by: Christoph Wurst --- lib/AppInfo/Application.php | 4 +- lib/Controller/SettingsController.php | 3 +- lib/Provider/U2FLoginProvider.php | 39 ++++++ lib/Provider/U2FProvider.php | 23 +++- package-lock.json | 213 +++++++++++++++++++------------- package.json | 2 + src/components/AddDeviceDialog.vue | 32 +++-- src/components/LoginSetup.vue | 69 +++++++++++ src/logger.js | 32 +++++ src/main-login-setup.js | 37 ++++++ src/tests/setup.js | 6 +- src/webpack.common.js | 3 +- templates/loginsetup.php | 28 +++++ tests/Unit/Provider/U2FProviderTest.php | 25 +++- 14 files changed, 412 insertions(+), 104 deletions(-) create mode 100644 lib/Provider/U2FLoginProvider.php create mode 100644 src/components/LoginSetup.vue create mode 100644 src/logger.js create mode 100644 src/main-login-setup.js create mode 100644 templates/loginsetup.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 5ecb7f8..a552036 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -24,8 +24,10 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Application extends App { + const APP_ID = 'twofactor_u2f'; + public function __construct(array $urlParams = []) { - parent::__construct('twofactor_u2f', $urlParams); + parent::__construct(self::APP_ID, $urlParams); $container = $this->getContainer(); /** @var EventDispatcherInterface $eventDispatcher */ diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index e7965dd..3d3e0dc 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -19,10 +19,11 @@ require_once(__DIR__ . '/../../vendor/yubico/u2flib-server/src/u2flib_server/U2F use OCA\TwoFactorU2F\Service\U2FManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; +use OCP\Authentication\TwoFactorAuth\ALoginSetupController; use OCP\IRequest; use OCP\IUserSession; -class SettingsController extends Controller { +class SettingsController extends ALoginSetupController { /** @var U2FManager */ private $manager; diff --git a/lib/Provider/U2FLoginProvider.php b/lib/Provider/U2FLoginProvider.php new file mode 100644 index 0000000..09649f9 --- /dev/null +++ b/lib/Provider/U2FLoginProvider.php @@ -0,0 +1,39 @@ + + * + * @author 2019 Christoph Wurst + * + * @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\TwoFactorU2F\Provider; + +use OCA\TwoFactorU2F\AppInfo\Application; +use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider; +use OCP\Template; + +class U2FLoginProvider implements ILoginSetupProvider { + + /** + * @return Template + */ + public function getBody(): Template { + return new Template(Application::APP_ID, 'loginsetup'); + } + +} diff --git a/lib/Provider/U2FProvider.php b/lib/Provider/U2FProvider.php index ab3f92a..c9e8b9b 100644 --- a/lib/Provider/U2FProvider.php +++ b/lib/Provider/U2FProvider.php @@ -16,16 +16,18 @@ namespace OCA\TwoFactorU2F\Provider; use OCA\TwoFactorU2F\Service\U2FManager; use OCA\TwoFactorU2F\Settings\Personal; +use OCP\AppFramework\IAppContainer; +use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin; use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin; +use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider; use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; -use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IProvidesIcons; use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings; use OCP\IL10N; use OCP\IUser; use OCP\Template; -class U2FProvider implements IProvider, IProvidesIcons, IProvidesPersonalSettings, IDeactivatableByAdmin { +class U2FProvider implements IActivatableAtLogin, IProvidesIcons, IProvidesPersonalSettings, IDeactivatableByAdmin { /** @var IL10N */ private $l10n; @@ -33,9 +35,15 @@ class U2FProvider implements IProvider, IProvidesIcons, IProvidesPersonalSetting /** @var U2FManager */ private $manager; - public function __construct(IL10N $l10n, U2FManager $manager) { + /** @var IAppContainer */ + private $container; + + public function __construct(IL10N $l10n, + U2FManager $manager, + IAppContainer $container) { $this->l10n = $l10n; $this->manager = $manager; + $this->container = $container; } /** @@ -105,4 +113,13 @@ class U2FProvider implements IProvider, IProvidesIcons, IProvidesPersonalSetting $this->manager->removeAllDevices($user); } + /** + * @param IUser $user + * + * @return ILoginSetupProvider + */ + public function getLoginSetup(IUser $user): ILoginSetupProvider { + return $this->container->query(U2FLoginProvider::class); + } + } diff --git a/package-lock.json b/package-lock.json index bd4e717..c571104 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2813,7 +2813,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, "requires": { "chalk": "^1.1.3", "esutils": "^2.0.2", @@ -2823,20 +2822,17 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -2848,14 +2844,12 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2863,8 +2857,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } }, @@ -2898,6 +2891,27 @@ } } }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, "babel-loader": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", @@ -2922,7 +2936,6 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, "requires": { "babel-runtime": "^6.22.0" } @@ -3132,11 +3145,26 @@ "resolve": "^1.4.0" } }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=" + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -3146,7 +3174,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, "requires": { "babel-runtime": "^6.26.0", "babel-traverse": "^6.26.0", @@ -3158,8 +3185,7 @@ "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" } } }, @@ -3167,7 +3193,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, "requires": { "babel-code-frame": "^6.26.0", "babel-messages": "^6.23.0", @@ -3183,14 +3208,12 @@ "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" } } }, @@ -3198,7 +3221,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, "requires": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", @@ -3209,8 +3231,7 @@ "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" } } }, @@ -3616,9 +3637,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -3999,8 +4020,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", - "dev": true + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" }, "core-js-compat": { "version": "3.1.4", @@ -4291,7 +4311,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -4580,8 +4599,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.11.1", @@ -4630,8 +4648,7 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "events": { "version": "3.0.0", @@ -5857,7 +5874,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" }, @@ -5865,8 +5881,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" } } }, @@ -6157,7 +6172,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -6477,9 +6491,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "supports-color": { @@ -6544,9 +6558,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -6569,8 +6583,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.13.1", @@ -6587,6 +6600,24 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" } } }, @@ -6835,7 +6866,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -7053,7 +7083,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -7113,9 +7143,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -7171,7 +7201,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -7289,7 +7319,7 @@ }, "yargs": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { @@ -7396,6 +7426,21 @@ "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", "dev": true }, + "nextcloud-auth": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/nextcloud-auth/-/nextcloud-auth-0.0.3.tgz", + "integrity": "sha512-qEAl55QJg2gZZIpfin9NzCPWm/Mfbo+HOdaXpsastPZw8oA7YLFFZon3x6SQ/p/LVIPQzRZmMpjd8R2FAAbjzg==", + "requires": { + "core-js": "^3.1.4" + }, + "dependencies": { + "core-js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.0.tgz", + "integrity": "sha512-gybgLzmr7SQRSF6UzGYXducx4eE10ONQlyEnQoqiGPbmbn7zLkb73tPfc4YbZN0lvcTQwoLNPjq4RuCaCumGyQ==" + } + } + }, "nextcloud-axios": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nextcloud-axios/-/nextcloud-axios-0.2.0.tgz", @@ -7404,6 +7449,22 @@ "axios": "^0.19.0" } }, + "nextcloud-logger": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/nextcloud-logger/-/nextcloud-logger-0.0.3.tgz", + "integrity": "sha512-bTCSzISptSPeZDdM030rc3M2wH2YEysBslSTmDmxwAlKZlBiDaca/swYfUO0y/fnxMfZuLh6Odr1EJXLgFVSrQ==", + "requires": { + "babel-plugin-transform-class-properties": "^6.24.1", + "core-js": "^3.1.4" + }, + "dependencies": { + "core-js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.0.tgz", + "integrity": "sha512-gybgLzmr7SQRSF6UzGYXducx4eE10ONQlyEnQoqiGPbmbn7zLkb73tPfc4YbZN0lvcTQwoLNPjq4RuCaCumGyQ==" + } + } + }, "nextcloud-password-confirmation": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/nextcloud-password-confirmation/-/nextcloud-password-confirmation-0.4.1.tgz", @@ -8674,8 +8735,7 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { "version": "0.14.0", @@ -9018,9 +9078,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9891,38 +9951,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -10595,7 +10632,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { diff --git a/package.json b/package.json index 45df398..6a5fa49 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "U2F second factor provider for Nextcloud", "private": true, "dependencies": { + "nextcloud-auth": "0.0.3", "nextcloud-axios": "^0.2.0", + "nextcloud-logger": "0.0.3", "nextcloud-password-confirmation": "^0.4.1", "nextcloud-router": "0.0.6", "nextcloud-vue": "^0.12.1", diff --git a/src/components/AddDeviceDialog.vue b/src/components/AddDeviceDialog.vue index 8a72126..78c5e8f 100644 --- a/src/components/AddDeviceDialog.vue +++ b/src/components/AddDeviceDialog.vue @@ -56,16 +56,26 @@ import confirmPassword from 'nextcloud-password-confirmation' import u2f from 'u2f-api' + import logger from '../logger' + import { startRegistration, finishRegistration } from '../services/RegistrationService' const logAndPass = (text) => (data) => { - console.debug(text) + logger.debug(text) return data } + /** + * Tap into a promise without losing the value + */ + const tap = cb => val => { + cb(val) + return val + } + const RegistrationSteps = Object.freeze({ READY: 1, U2F_REGISTRATION: 2, @@ -94,23 +104,23 @@ .then(this.getRegistrationData) .then(this.register) .then(() => this.step = RegistrationSteps.NAMING) - .catch(console.error.bind(this)) + .catch(logger.error.bind(logger)) }, getRegistrationData () { return startRegistration() .catch(err => { - console.error('Error getting u2f registration data from server', err) + logger.error('Error getting u2f registration data from server', err) throw new Error(t('twofactor_u2f', 'Server error while trying to add U2F device')) }) }, register ({req, sigs}) { - console.debug('starting u2f registration') + logger.debug('starting registration') return u2f.register([req], sigs) .then(data => { - console.debug('u2f registration was successful', data) + logger.debug('registration was successful', data) if (data.errorCode && data.errorCode !== 0) { return this.rejectRegistration(data) @@ -118,6 +128,7 @@ this.registrationData = data }) + .catch(e => this.rejectRegistration(e)) }, rejectRegistration (data) { @@ -148,24 +159,29 @@ submit () { this.step = RegistrationSteps.PERSIST + logger.debug('persisting registration on server') + return confirmPassword() .then(logAndPass('confirmed password')) .then(this.saveRegistrationData) .then(logAndPass('registration data saved')) + .then(tap(() => this.$emit('add'))) .then(() => this.reset()) .then(logAndPass('app reset')) - .catch(console.error.bind(this)) + .catch(logger.error.bind(logger)) }, saveRegistrationData () { const data = this.registrationData data.name = this.name + logger.debug('saving registration data', {data}) + return finishRegistration(data) .then(device => this.$store.commit('addDevice', device)) .then(logAndPass('new device added to store')) .catch(err => { - console.error('Error persisting u2f registration', err) + logger.error('Error persisting registration', err) throw new Error(t('twofactor_u2f', 'Server error while trying to complete U2F device registration')) }) }, @@ -190,4 +206,4 @@ .new-u2f-device { line-height: 300%; } - \ No newline at end of file + diff --git a/src/components/LoginSetup.vue b/src/components/LoginSetup.vue new file mode 100644 index 0000000..537beb5 --- /dev/null +++ b/src/components/LoginSetup.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/logger.js b/src/logger.js new file mode 100644 index 0000000..5efdf66 --- /dev/null +++ b/src/logger.js @@ -0,0 +1,32 @@ +/* + * @copyright 2019 Christoph Wurst + * + * @author 2019 Christoph Wurst + * + * @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 . + */ + +import { getCurrentUser } from 'nextcloud-auth' +import { getLoggerBuilder } from 'nextcloud-logger' + +const builder = getLoggerBuilder().setApp('twofactor_u2f') + +const user = getCurrentUser() +if (user !== null) { + builder.setUid(user.uid) +} + +export default builder.build() diff --git a/src/main-login-setup.js b/src/main-login-setup.js new file mode 100644 index 0000000..2a27a6c --- /dev/null +++ b/src/main-login-setup.js @@ -0,0 +1,37 @@ +/* + * @copyright 2019 Christoph Wurst + * + * @author 2019 Christoph Wurst + * + * @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 . + */ + +import Vue from 'vue' + +import Nextcloud from './mixins/Nextcloud' +import store from './store' + +Vue.mixin(Nextcloud) + +import LoginSetup from './components/LoginSetup' + +const View = Vue.extend(LoginSetup) +new View({ + propsData: { + httpWarning: document.location.protocol !== 'https:', + }, + store, +}).$mount('#twofactor-u2f-login-setup') diff --git a/src/tests/setup.js b/src/tests/setup.js index 59a3c57..451b349 100644 --- a/src/tests/setup.js +++ b/src/tests/setup.js @@ -30,7 +30,11 @@ require('vue').mixin({ }) global.expect = require('chai').expect -global.OC = {} +global.OC = { + getCurrentUser: () => { + return { uid: false } + }, +} global.t = t // https://github.com/vuejs/vue-test-utils/issues/936 diff --git a/src/webpack.common.js b/src/webpack.common.js index 4940550..8dc3d21 100644 --- a/src/webpack.common.js +++ b/src/webpack.common.js @@ -4,7 +4,8 @@ const { VueLoaderPlugin } = require('vue-loader'); module.exports = { entry: { challenge: path.join(__dirname, 'main-challenge.js'), - settings: path.join(__dirname, 'main-settings.js') + 'login-setup': path.join(__dirname, 'main-login-setup.js'), + settings: path.join(__dirname, 'main-settings.js'), }, output: { path: path.resolve(__dirname, '../js'), diff --git a/templates/loginsetup.php b/templates/loginsetup.php new file mode 100644 index 0000000..d603f0a --- /dev/null +++ b/templates/loginsetup.php @@ -0,0 +1,28 @@ + + * + * @author 2019 Christoph Wurst + * + * @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 . + */ + +script('twofactor_u2f', 'login-setup'); + +?> + +
diff --git a/tests/Unit/Provider/U2FProviderTest.php b/tests/Unit/Provider/U2FProviderTest.php index b6f99c6..6f6df28 100644 --- a/tests/Unit/Provider/U2FProviderTest.php +++ b/tests/Unit/Provider/U2FProviderTest.php @@ -12,9 +12,11 @@ namespace OCA\TwoFactorU2F\Tests\Unit\Provider; +use OCA\TwoFactorU2F\Provider\U2FLoginProvider; use OCA\TwoFactorU2F\Provider\U2FProvider; use OCA\TwoFactorU2F\Service\U2FManager; use OCA\TwoFactorU2F\Settings\Personal; +use OCP\AppFramework\IAppContainer; use OCP\IL10N; use OCP\IUser; use OCP\Template; @@ -29,6 +31,9 @@ class U2FProviderTest extends TestCase { /** @var U2FManager|MockObject */ private $manager; + /** @var IAppContainer|MockObject */ + private $container; + /** @var U2FProvider */ private $provider; @@ -37,8 +42,13 @@ class U2FProviderTest extends TestCase { $this->l10n = $this->createMock(IL10N::class); $this->manager = $this->createMock(U2FManager::class); + $this->container = $this->createMock(IAppContainer::class); - $this->provider = new U2FProvider($this->l10n, $this->manager); + $this->provider = new U2FProvider( + $this->l10n, + $this->manager, + $this->container + ); } public function testGetId() { @@ -148,4 +158,17 @@ class U2FProviderTest extends TestCase { $this->provider->disableFor($user); } + public function testGet() { + $user = $this->createMock(IUser::class); + $loginProvider = $this->createMock(U2FLoginProvider::class); + $this->container->expects($this->once()) + ->method('query') + ->with(U2FLoginProvider::class) + ->willReturn($loginProvider); + + $result = $this->provider->getLoginSetup($user); + + $this->assertSame($loginProvider, $result); + } + } -- cgit v1.2.3