]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Add backup codes
authorJeremy Benoist <jeremy.benoist@gmail.com>
Mon, 3 Dec 2018 05:51:06 +0000 (06:51 +0100)
committerJeremy Benoist <jeremy.benoist@gmail.com>
Wed, 23 Jan 2019 12:28:03 +0000 (13:28 +0100)
21 files changed:
app/DoctrineMigrations/Version20181202073750.php
app/config/config.yml
composer.json
src/Wallabag/CoreBundle/Controller/ConfigController.php
src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
src/Wallabag/UserBundle/Entity/User.php

index a2308b99840f732379c45e0d2c6d5941725246a5..b6ad8bd73edad633b7776834f18cff27e12fe02e 100644 (file)
@@ -12,11 +12,29 @@ final class Version20181202073750 extends WallabagMigration
 {
     public function up(Schema $schema): void
     {
-        $this->addSql('ALTER TABLE ' . $this->getTable('user') . ' ADD googleAuthenticatorSecret VARCHAR(191) DEFAULT NULL, CHANGE twoFactorAuthentication emailTwoFactor BOOLEAN NOT NULL, DROP trusted');
+        $tableName = $this->getTable('annotation');
+
+        switch ($this->connection->getDatabasePlatform()->getName()) {
+            case 'sqlite':
+                break;
+            case 'mysql':
+                $this->addSql('ALTER TABLE ' . $this->getTable('user') . ' ADD googleAuthenticatorSecret VARCHAR(191) DEFAULT NULL, CHANGE twoFactorAuthentication emailTwoFactor BOOLEAN NOT NULL, DROP trusted, ADD backupCodes LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:json_array)\'');
+                break;
+            case 'postgresql':
+                break;
+        }
     }
 
     public function down(Schema $schema): void
     {
-        $this->addSql('ALTER TABLE `' . $this->getTable('user') . '` DROP googleAuthenticatorSecret, CHANGE emailtwofactor twoFactorAuthentication BOOLEAN NOT NULL, ADD trusted TEXT DEFAULT NULL');
+        switch ($this->connection->getDatabasePlatform()->getName()) {
+            case 'sqlite':
+                break;
+            case 'mysql':
+                $this->addSql('ALTER TABLE `' . $this->getTable('user') . '` DROP googleAuthenticatorSecret, CHANGE emailtwofactor twoFactorAuthentication BOOLEAN NOT NULL, ADD trusted TEXT DEFAULT NULL, DROP backupCodes');
+                break;
+            case 'postgresql':
+                break;
+        }
     }
 }
index 908f53b7e2c4d985937ab4021f06af8453f326dd..2d8f9bf01eae24a7c1e7dd243f9e42179bd11e73 100644 (file)
@@ -203,6 +203,9 @@ scheb_two_factor:
         cookie_name: wllbg_trusted_computer
         lifetime: 2592000
 
+    backup_codes:
+        enabled: "%twofactor_auth%"
+
     google:
         enabled: "%twofactor_auth%"
         template: WallabagUserBundle:Authentication:form.html.twig
index 771580c672e49050cc705562920bcd94a53190e9..7678d7b87b695b4380507b9eb5fdc430c8b9c356 100644 (file)
@@ -87,7 +87,8 @@
         "friendsofsymfony/jsrouting-bundle": "^2.2",
         "bdunogier/guzzle-site-authenticator": "^1.0.0",
         "defuse/php-encryption": "^2.1",
-        "html2text/html2text": "^4.1"
+        "html2text/html2text": "^4.1",
+        "pragmarx/recovery": "^0.1.0"
     },
     "require-dev": {
         "doctrine/doctrine-fixtures-bundle": "~3.0",
index 846e96ff9a5c9807dc5137a1cd504379d2a25c56..c9fc570268c7fc09c21e4413cc046b5525396b3d 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Wallabag\CoreBundle\Controller;
 
+use PragmaRX\Recovery\Recovery as BackupCodes;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -93,10 +94,12 @@ class ConfigController extends Controller
 
                     $user->setGoogleAuthenticatorSecret($secret);
                     $user->setEmailTwoFactor(false);
+                    $user->setBackupCodes((new BackupCodes())->toArray());
 
                     $this->addFlash('OtpQrCode', $this->get('scheb_two_factor.security.google_authenticator')->getQRContent($user));
                 } elseif (false === $userForm->get('googleTwoFactor')->getData() && true === $user->isGoogleAuthenticatorEnabled()) {
                     $user->setGoogleAuthenticatorSecret(null);
+                    $user->setBackupCodes(null);
                 }
             }
 
index d3e96e5c5bc76297dfd0f073648ac33689209ce7..0114a983bcf37fd04ea6b4b9dfd23b9ed9f5ef1b 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             # title: Delete my account (a.k.a danger zone)
             # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index 9aeddceba81e02e9edca8fb4f10293f8d5a4ad55..fd9796ba8e34d113564708a4aee462c83e67ece0 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: 'Lösche mein Konto (a.k.a Gefahrenzone)'
             description: 'Wenn du dein Konto löschst, werden ALL deine Artikel, ALL deine Tags, ALL deine Anmerkungen und dein Konto dauerhaft gelöscht (kann NICHT RÜCKGÄNGIG gemacht werden). Du wirst anschließend ausgeloggt.'
index 22c68c799bd426935c2a7689f76ba974e4cf4f07..ddc079ed090ea03141ba1524d8d8d93a809f8ec1 100644 (file)
@@ -107,6 +107,7 @@ config:
         two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         two_factor_code_description_2: 'You can scan that QR Code with your app:'
         two_factor_code_description_3: 'Or use that code:'
+        two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: Delete my account (a.k.a danger zone)
             description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index 6e710e56aa9c4156d9aa920822bed801bf99a9c9..8ac661692a10a49ecfcfae026a10279f05df07cf 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: Eliminar mi cuenta (Zona peligrosa)
             description: Si eliminas tu cuenta, TODOS tus artículos, TODAS tus etiquetas, TODAS tus anotaciones y tu cuenta serán eliminadas de forma PERMANENTE (no se puede deshacer). Después serás desconectado.
index 855f38cc8abb8b9de440b670482b9db13cd11837..bc754ca21eb888ec38f3512710754a2a8841c697 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             # title: Delete my account (a.k.a danger zone)
             # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index f92b64a56bd37165d43cf741c15c630bb7998c0f..288411451775e33b0378ec97be0989090b814ae1 100644 (file)
@@ -107,6 +107,7 @@ config:
         two_factor_code_description_1: Vous venez d’activer l’authentification double-facteur, ouvrez votre application OTP pour configurer la génération du mot de passe à usage unique. Ces informations disparaîtront après un rechargement de la page.
         two_factor_code_description_2: 'Vous pouvez scanner le QR code avec votre application :'
         two_factor_code_description_3: 'Ou utiliser le code suivant :'
+        two_factor_code_description_4: 'N’oubliez pas de sauvegarder ces codes de secours dans un endroit sûr, vous pourrez les utiliser si vous ne pouvez plus accéder à votre application OTP :'
         delete:
             title: "Supprimer mon compte (attention danger !)"
             description: "Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c’est IRRÉVERSIBLE). Vous serez ensuite déconnecté."
index 95d4ac200efd0ca276e1abf396ceac75f19d1141..b78dcb322e3a4067aaf05ff10056d70dfff406fe 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: Cancella il mio account (zona pericolosa)
             description: Rimuovendo il tuo account, TUTTI i tuoi articoli, TUTTE le tue etichette, TUTTE le tue annotazioni ed il tuo account verranno rimossi PERMANENTEMENTE (impossibile da ANNULLARE). Verrai poi disconnesso.
index 96725a0695c118e5790883aa37fede2532638e76..c1f57bc72efdb97b0108bc7519010ddeea56e0bd 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: Suprimir mon compte (Mèfi zòna perilhosa)
             description: Se confirmatz la supression de vòstre compte, TOTES vòstres articles, TOTAS vòstras etiquetas, TOTAS vòstras anotacions e vòstre compte seràn suprimits per totjorn. E aquò es IRREVERSIBLE. Puèi seretz desconnectat.
index 5f77061c0bb06ac9a005c983a634f22200b9becc..2dc8d8547bd90518d9b6f4f2fbf72ca91c4237ec 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: Usuń moje konto (niebezpieczna strefa !)
             description: Jeżeli usuniesz swoje konto, wszystkie twoje artykuły, tagi, adnotacje, oraz konto zostaną trwale usunięte (operacja jest NIEODWRACALNA). Następnie zostaniesz wylogowany.
index f40f97958326ce4bb3560ccafe34d2b85a0abe93..a81d8d0df1c57317190564117cafbe1d83b1c3d7 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             # title: Delete my account (a.k.a danger zone)
             # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index 369d2d44a98aa41d1de343b3ec684b55ec0fe687..fd56581959283c573a4f7071d533807bb97de7f6 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             # title: Delete my account (a.k.a danger zone)
             # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index d9b33fed07ae5a126feaaf7283bf777982b6a976..5a0c5445131e95ca83b093d80f2817af95a67f71 100644 (file)
@@ -104,6 +104,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: "Удалить мой аккаунт (или опасная зона)"
             description: "Если Вы удалите ваш аккаунт, ВСЕ ваши записи, теги и другие данные, будут БЕЗВОЗВРАТНО удалены (операция не может быть отменена после). Затем Вы выйдете из системы."
index f25bac8463618840b0f408f378f76ed3bf5d97e6..a69b500852591eca7670f92fa1dff94467967c0f 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             title: ลบบัญชีของฉัน (โซนที่เป็นภัย!)
             description: ถ้าคุณลบบัญชีของคุณIf , รายการทั้งหมดของคุณ, แท็กทั้งหมดของคุณ, หมายเหตุทั้งหมดของคุณและบัญชีของคุณจะถูกลบอย่างถาวร (มันไม่สามารถยกเลิกได้) คุณจะต้องลงชื่อออก
index d65fc00166d42a01ac25bceefff576ce14fbddfb..0c3d84e9be9e68a8103f0d2daebafa496e8f98fe 100644 (file)
@@ -107,6 +107,7 @@ config:
         # two_factor_code_description_1: You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password. It'll disapear after a page reload.
         # two_factor_code_description_2: 'You can scan that QR Code with your app:'
         # two_factor_code_description_3: 'Or use that code:'
+        # two_factor_code_description_4: 'Also, save these backup codes in a safe place, you can use them in case you lose access to your OTP app:'
         delete:
             # title: Delete my account (a.k.a danger zone)
             # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
index 6ee574436d684ecefec5bde04c1c06bb1f5ea077..cf4394081b867875bacdd35027903bd1d61a8bc8 100644 (file)
             </div>
             {% for OtpQrCode in app.session.flashbag.get('OtpQrCode') %}
                 <div class="row">
-                    You just enabled the OTP two factor authentication, open your OTP app and use that code to get a one time password.
+                    {{ 'config.form_user.two_factor_code_description_1'|trans }}
                     <br/>
-                    That code will disapear after a page reload.
+                    {{ 'config.form_user.two_factor_code_description_2'|trans }}
                     <br/><br/>
-                    <strong>{{ app.user.getGoogleAuthenticatorSecret }}</strong>
-                    <br/><br/>
-                    Or you can scan that QR Code with your app:
-                    <br/>
                     <img id="2faQrcode" class="hide-on-med-and-down" />
-
                     <script>
-                        document.getElementById('2faQrcode').src = jrQrcode.getQrBase64('{{ OtpQrCode }}');;
+                        document.getElementById('2faQrcode').src = jrQrcode.getQrBase64('{{ OtpQrCode }}');
                     </script>
+                    <br/><br/>
+                    {{ 'config.form_user.two_factor_code_description_3'|trans }}
+                    <br/><br/>
+                    <strong>{{ app.user.getGoogleAuthenticatorSecret }}</strong>
+                    <br/><br/>
+                    {{ 'config.form_user.two_factor_code_description_4'|trans }}
+                    <br/><br/>
+                    <strong>{{ app.user.getBackupCodes|join("\n")|nl2br }}</strong>
                 </div>
             {% endfor %}
         </fieldset>
index 73cf592eabd196a35a459b24fb735f6d158fa220..5b00eb7bd1e011cf61c4c00f3d69f2fee3c59484 100644 (file)
                                     <img id="androidQrcode" class="hide-on-med-and-down" />
                                 </div>
                                 <script>
-                                    document.getElementById('androidQrcode').src = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');;
+                                    document.getElementById('androidQrcode').src = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
                                 </script>
                             </div>
 
                                         <br/><br/>
                                         <img id="2faQrcode" class="hide-on-med-and-down" />
                                         <script>
-                                            document.getElementById('2faQrcode').src = jrQrcode.getQrBase64('{{ OtpQrCode }}');;
+                                            document.getElementById('2faQrcode').src = jrQrcode.getQrBase64('{{ OtpQrCode }}');
                                         </script>
                                         <br/><br/>
                                         {{ 'config.form_user.two_factor_code_description_3'|trans }}
                                         <br/><br/>
                                         <strong>{{ app.user.getGoogleAuthenticatorSecret }}</strong>
+                                        <br/><br/>
+                                        {{ 'config.form_user.two_factor_code_description_4'|trans }}
+                                        <br/><br/>
+                                        <strong>{{ app.user.getBackupCodes|join("\n")|nl2br }}</strong>
                                     </div>
                                 {% endfor %}
                             {% endif %}
index 6e305719fb0cd2daf4e506bb886ad7cc1f16fcb9..ab34e2bfc956d55cf1f3705ceb48cd3a535a22ef 100644 (file)
@@ -8,6 +8,7 @@ use FOS\UserBundle\Model\User as BaseUser;
 use JMS\Serializer\Annotation\Accessor;
 use JMS\Serializer\Annotation\Groups;
 use JMS\Serializer\Annotation\XmlRoot;
+use Scheb\TwoFactorBundle\Model\BackupCodeInterface;
 use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface as EmailTwoFactorInterface;
 use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface as GoogleTwoFactorInterface;
 use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
@@ -28,7 +29,7 @@ use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
  * @UniqueEntity("email")
  * @UniqueEntity("username")
  */
-class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorInterface
+class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorInterface, BackupCodeInterface
 {
     use EntityTimestampsTrait;
 
@@ -127,6 +128,11 @@ class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorI
      */
     private $googleAuthenticatorSecret;
 
+    /**
+     * @ORM\Column(type="json_array", nullable=true)
+     */
+    private $backupCodes;
+
     /**
      * @var bool
      *
@@ -318,6 +324,36 @@ class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorI
         $this->googleAuthenticatorSecret = $googleAuthenticatorSecret;
     }
 
+    public function setBackupCodes(array $codes = null)
+    {
+        $this->backupCodes = $codes;
+    }
+
+    public function getBackupCodes()
+    {
+        return $this->backupCodes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isBackupCode(string $code): bool
+    {
+        return \in_array($code, $this->backupCodes, true);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function invalidateBackupCode(string $code): void
+    {
+        $key = array_search($code, $this->backupCodes, true);
+
+        if (false !== $key) {
+            unset($this->backupCodes[$key]);
+        }
+    }
+
     /**
      * @param Client $client
      *