From efb6a6b457ac8e20bbbb39d8a730921850c2751a Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Mon, 28 Jun 2021 22:02:45 +0100
Subject: [PATCH] Started barebones work of MFA system

---
 app/Http/Controllers/Auth/MfaController.php |  29 +++
 composer.json                               |   2 +
 composer.lock                               | 221 +++++++++++++++++++-
 resources/views/mfa/setup.blade.php         |  16 ++
 routes/web.php                              |   2 +
 5 files changed, 269 insertions(+), 1 deletion(-)
 create mode 100644 app/Http/Controllers/Auth/MfaController.php
 create mode 100644 resources/views/mfa/setup.blade.php

diff --git a/app/Http/Controllers/Auth/MfaController.php b/app/Http/Controllers/Auth/MfaController.php
new file mode 100644
index 000000000..1d0dbd1a4
--- /dev/null
+++ b/app/Http/Controllers/Auth/MfaController.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace BookStack\Http\Controllers\Auth;
+
+use BookStack\Http\Controllers\Controller;
+use Illuminate\Http\Request;
+
+class MfaController extends Controller
+{
+    /**
+     * Show the view to setup MFA for the current user.
+     */
+    public function setup()
+    {
+        // TODO - Redirect back to profile/edit if already setup?
+        // Show MFA setup route
+        return view('mfa.setup');
+    }
+
+    public function generateQr()
+    {
+        // https://github.com/antonioribeiro/google2fa#how-to-generate-and-use-two-factor-authentication
+
+        // Generate secret key
+        // Store key in session?
+        // Get user to verify setup via responding once.
+        // If correct response, Save key against user
+    }
+}
diff --git a/composer.json b/composer.json
index bbd689454..7362a085d 100644
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
         "ext-json": "*",
         "ext-mbstring": "*",
         "ext-xml": "*",
+        "bacon/bacon-qr-code": "^2.0",
         "barryvdh/laravel-dompdf": "^0.9.0",
         "barryvdh/laravel-snappy": "^0.4.8",
         "doctrine/dbal": "^2.12.1",
@@ -26,6 +27,7 @@
         "league/html-to-markdown": "^5.0.0",
         "nunomaduro/collision": "^3.1",
         "onelogin/php-saml": "^4.0",
+        "pragmarx/google2fa": "^8.0",
         "predis/predis": "^1.1.6",
         "socialiteproviders/discord": "^4.1",
         "socialiteproviders/gitlab": "^4.1",
diff --git a/composer.lock b/composer.lock
index 0647038cc..07c27ea52 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "d1109d0dc4a6ab525cdbf64ed21f6dd4",
+    "content-hash": "4d845f3c8b77c8d73bf92c9223ddd805",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
@@ -96,6 +96,59 @@
             },
             "time": "2021-06-25T18:19:14+00:00"
         },
+        {
+            "name": "bacon/bacon-qr-code",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Bacon/BaconQrCode.git",
+                "reference": "f73543ac4e1def05f1a70bcd1525c8a157a1ad09"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f73543ac4e1def05f1a70bcd1525c8a157a1ad09",
+                "reference": "f73543ac4e1def05f1a70bcd1525c8a157a1ad09",
+                "shasum": ""
+            },
+            "require": {
+                "dasprid/enum": "^1.0.3",
+                "ext-iconv": "*",
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "phly/keep-a-changelog": "^1.4",
+                "phpunit/phpunit": "^7 | ^8 | ^9",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "suggest": {
+                "ext-imagick": "to generate QR code images"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "BaconQrCode\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "BaconQrCode is a QR code generator for PHP.",
+            "homepage": "https://github.com/Bacon/BaconQrCode",
+            "support": {
+                "issues": "https://github.com/Bacon/BaconQrCode/issues",
+                "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.4"
+            },
+            "time": "2021-06-18T13:26:35+00:00"
+        },
         {
             "name": "barryvdh/laravel-dompdf",
             "version": "v0.9.0",
@@ -227,6 +280,53 @@
             },
             "time": "2020-09-07T12:33:10+00:00"
         },
+        {
+            "name": "dasprid/enum",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/DASPRiD/Enum.git",
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
+                "shasum": ""
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7 | ^8 | ^9",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DASPRiD\\Enum\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP 7.1 enum implementation",
+            "keywords": [
+                "enum",
+                "map"
+            ],
+            "support": {
+                "issues": "https://github.com/DASPRiD/Enum/issues",
+                "source": "https://github.com/DASPRiD/Enum/tree/1.0.3"
+            },
+            "time": "2020-10-02T16:03:48+00:00"
+        },
         {
             "name": "doctrine/cache",
             "version": "2.0.3",
@@ -2789,6 +2889,73 @@
             },
             "time": "2021-04-09T13:42:10+00:00"
         },
+        {
+            "name": "paragonie/constant_time_encoding",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/constant_time_encoding.git",
+                "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c",
+                "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7|^8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6|^7|^8|^9",
+                "vimeo/psalm": "^1|^2|^3|^4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "ParagonIE\\ConstantTime\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "security@paragonie.com",
+                    "homepage": "https://paragonie.com",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Steve 'Sc00bz' Thomas",
+                    "email": "steve@tobtu.com",
+                    "homepage": "https://www.tobtu.com",
+                    "role": "Original Developer"
+                }
+            ],
+            "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
+            "keywords": [
+                "base16",
+                "base32",
+                "base32_decode",
+                "base32_encode",
+                "base64",
+                "base64_decode",
+                "base64_encode",
+                "bin2hex",
+                "encoding",
+                "hex",
+                "hex2bin",
+                "rfc4648"
+            ],
+            "support": {
+                "email": "info@paragonie.com",
+                "issues": "https://github.com/paragonie/constant_time_encoding/issues",
+                "source": "https://github.com/paragonie/constant_time_encoding"
+            },
+            "time": "2020-12-06T15:14:20+00:00"
+        },
         {
             "name": "paragonie/random_compat",
             "version": "v9.99.99",
@@ -3095,6 +3262,58 @@
             ],
             "time": "2020-07-20T17:29:33+00:00"
         },
+        {
+            "name": "pragmarx/google2fa",
+            "version": "8.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/antonioribeiro/google2fa.git",
+                "reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/26c4c5cf30a2844ba121760fd7301f8ad240100b",
+                "reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/constant_time_encoding": "^1.0|^2.0",
+                "php": "^7.1|^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.18",
+                "phpunit/phpunit": "^7.5.15|^8.5|^9.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PragmaRX\\Google2FA\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Antonio Carlos Ribeiro",
+                    "email": "acr@antoniocarlosribeiro.com",
+                    "role": "Creator & Designer"
+                }
+            ],
+            "description": "A One Time Password Authentication package, compatible with Google Authenticator.",
+            "keywords": [
+                "2fa",
+                "Authentication",
+                "Two Factor Authentication",
+                "google2fa"
+            ],
+            "support": {
+                "issues": "https://github.com/antonioribeiro/google2fa/issues",
+                "source": "https://github.com/antonioribeiro/google2fa/tree/8.0.0"
+            },
+            "time": "2020-04-05T10:47:18+00:00"
+        },
         {
             "name": "predis/predis",
             "version": "v1.1.7",
diff --git a/resources/views/mfa/setup.blade.php b/resources/views/mfa/setup.blade.php
new file mode 100644
index 000000000..25eb5d925
--- /dev/null
+++ b/resources/views/mfa/setup.blade.php
@@ -0,0 +1,16 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small py-xl">
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">Setup Multi-Factor Authentication</h1>
+            <p>
+                Setup multi-factor authentication as an extra layer of security
+                for your user account.
+                To use multi-factor authentication you'll need a mobile application
+                that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.
+            </p>
+        </div>
+    </div>
+@stop
diff --git a/routes/web.php b/routes/web.php
index bc9705e10..7807d5477 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -223,6 +223,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/roles/{id}', 'RoleController@edit');
         Route::put('/roles/{id}', 'RoleController@update');
     });
+
+    Route::get('/mfa/setup', 'Auth\MfaController@setup');
 });
 
 // Social auth routes