]> BookStack Code Mirror - bookstack/commitdiff
Change email confirmation from own middle to trait
authorDan Brown <redacted>
Mon, 30 Dec 2019 15:46:12 +0000 (15:46 +0000)
committerDan Brown <redacted>
Mon, 30 Dec 2019 15:49:20 +0000 (15:49 +0000)
Email confirmation middleware caused more mess than good, As caused
priority issues and it depended on auth actions. Instead its now a trai
used on auth middlewares.

Also used 'EncryptCookies' middleware on API instead of custom
decryption in custom middleware since we'd need to do replicate all the
same actions anyway. Shouldn't have too much effect since it only
actions over cookies that exist, of which none should be there for most
API requests.

Also split out some large guard functions to be a little more readable
and appease codeclimate.

app/Api/ApiTokenGuard.php
app/Http/Kernel.php
app/Http/Middleware/ApiAuthenticate.php
app/Http/Middleware/Authenticate.php
app/Http/Middleware/ChecksForEmailConfirmation.php [new file with mode: 0644]
app/Http/Middleware/ConfirmEmails.php [deleted file]
app/Http/Middleware/StartSessionIfCookieExists.php

index b347e536acb67e230e803065e271daaafb5a209a..cd9c3b178c7e0ae01bc9b436fdd24e1561e0c25f 100644 (file)
@@ -33,8 +33,7 @@ class ApiTokenGuard implements Guard
     {
         $this->request = $request;
     }
-
-
+    
     /**
      * @inheritDoc
      */
@@ -84,6 +83,24 @@ class ApiTokenGuard implements Guard
     protected function getAuthorisedUserFromRequest(): Authenticatable
     {
         $authToken = trim($this->request->headers->get('Authorization', ''));
+        $this->validateTokenHeaderValue($authToken);
+
+        [$id, $secret] = explode(':', str_replace('Token ', '', $authToken));
+        $token = ApiToken::query()
+            ->where('token_id', '=', $id)
+            ->with(['user'])->first();
+
+        $this->validateToken($token, $secret);
+
+        return $token->user;
+    }
+
+    /**
+     * Validate the format of the token header value string.
+     * @throws ApiAuthException
+     */
+    protected function validateTokenHeaderValue(string $authToken): void
+    {
         if (empty($authToken)) {
             throw new ApiAuthException(trans('errors.api_no_authorization_found'));
         }
@@ -91,12 +108,15 @@ class ApiTokenGuard implements Guard
         if (strpos($authToken, ':') === false || strpos($authToken, 'Token ') !== 0) {
             throw new ApiAuthException(trans('errors.api_bad_authorization_format'));
         }
+    }
 
-        [$id, $secret] = explode(':', str_replace('Token ', '', $authToken));
-        $token = ApiToken::query()
-            ->where('token_id', '=', $id)
-            ->with(['user'])->first();
-
+    /**
+     * Validate the given secret against the given token and ensure the token
+     * currently has access to the instance API.
+     * @throws ApiAuthException
+     */
+    protected function validateToken(?ApiToken $token, string $secret): void
+    {
         if ($token === null) {
             throw new ApiAuthException(trans('errors.api_user_token_not_found'));
         }
@@ -108,8 +128,6 @@ class ApiTokenGuard implements Guard
         if (!$token->user->can('access-api')) {
             throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
         }
-
-        return $token->user;
     }
 
     /**
index 6a6e736b9f5f0374c7dafa8ea8fff327d73b9d9e..978583a7fef0b04ad91c59cceb6c39a82049d087 100644 (file)
@@ -13,26 +13,6 @@ class Kernel extends HttpKernel
         \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
         \BookStack\Http\Middleware\TrimStrings::class,
         \BookStack\Http\Middleware\TrustProxies::class,
-
-    ];
-
-    /**
-     * The priority ordering of middleware.
-     */
-    protected $middlewarePriority = [
-        \BookStack\Http\Middleware\EncryptCookies::class,
-        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
-        \Illuminate\Session\Middleware\StartSession::class,
-        \BookStack\Http\Middleware\StartSessionIfCookieExists::class,
-        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
-        \Illuminate\Routing\Middleware\ThrottleRequests::class,
-        \BookStack\Http\Middleware\VerifyCsrfToken::class,
-        \Illuminate\Routing\Middleware\SubstituteBindings::class,
-        \BookStack\Http\Middleware\Localization::class,
-        \BookStack\Http\Middleware\GlobalViewData::class,
-        \BookStack\Http\Middleware\Authenticate::class,
-        \BookStack\Http\Middleware\ApiAuthenticate::class,
-        \BookStack\Http\Middleware\ConfirmEmails::class,
     ];
 
     /**
@@ -50,13 +30,12 @@ class Kernel extends HttpKernel
             \BookStack\Http\Middleware\VerifyCsrfToken::class,
             \BookStack\Http\Middleware\Localization::class,
             \BookStack\Http\Middleware\GlobalViewData::class,
-            \BookStack\Http\Middleware\ConfirmEmails::class,
         ],
         'api' => [
             'throttle:60,1',
+            \BookStack\Http\Middleware\EncryptCookies::class,
             \BookStack\Http\Middleware\StartSessionIfCookieExists::class,
             \BookStack\Http\Middleware\ApiAuthenticate::class,
-            \BookStack\Http\Middleware\ConfirmEmails::class,
         ],
     ];
 
index 86fb83d58444b49f42f1ea505e36c76a121c11eb..c7fed405cc41e77b4176225a386b497e9793bc63 100644 (file)
@@ -3,11 +3,12 @@
 namespace BookStack\Http\Middleware;
 
 use BookStack\Exceptions\ApiAuthException;
-use BookStack\Http\Request;
 use Closure;
+use Illuminate\Http\Request;
 
 class ApiAuthenticate
 {
+    use ChecksForEmailConfirmation;
 
     /**
      * Handle an incoming request.
@@ -17,6 +18,9 @@ class ApiAuthenticate
         // Return if the user is already found to be signed in via session-based auth.
         // This is to make it easy to browser the API via browser after just logging into the system.
         if (signedInUser()) {
+            if ($this->awaitingEmailConfirmation()) {
+                return $this->emailConfirmationErrorResponse($request);
+            }
             return $next($request);
         }
 
@@ -30,6 +34,10 @@ class ApiAuthenticate
             return $this->unauthorisedResponse($exception->getMessage(), $exception->getCode());
         }
 
+        if ($this->awaitingEmailConfirmation()) {
+            return $this->emailConfirmationErrorResponse($request);
+        }
+
         return $next($request);
     }
 
index 40acc254b85db6f388f47dc18dbb6915eec11ccd..a171a8a2d4c2b9219376d60fa82d2b2a71d48572 100644 (file)
@@ -2,16 +2,22 @@
 
 namespace BookStack\Http\Middleware;
 
-use BookStack\Http\Request;
 use Closure;
+use Illuminate\Http\Request;
 
 class Authenticate
 {
+    use ChecksForEmailConfirmation;
+
     /**
      * Handle an incoming request.
      */
     public function handle(Request $request, Closure $next)
     {
+        if ($this->awaitingEmailConfirmation()) {
+            return $this->emailConfirmationErrorResponse($request);
+        }
+
         if (!hasAppAccess()) {
             if ($request->ajax()) {
                 return response('Unauthorized.', 401);
diff --git a/app/Http/Middleware/ChecksForEmailConfirmation.php b/app/Http/Middleware/ChecksForEmailConfirmation.php
new file mode 100644 (file)
index 0000000..684a7e9
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Illuminate\Http\Request;
+
+trait ChecksForEmailConfirmation
+{
+
+    /**
+     * Check if email confirmation is required and the current user is awaiting confirmation.
+     */
+    protected function awaitingEmailConfirmation(): bool
+    {
+        if (auth()->check()) {
+            $requireConfirmation = (setting('registration-confirmation') || setting('registration-restrict'));
+            if ($requireConfirmation && !auth()->user()->email_confirmed) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Provide an error response for when the current user's email is not confirmed
+     * in a system which requires it.
+     */
+    protected function emailConfirmationErrorResponse(Request $request)
+    {
+        if ($request->wantsJson()) {
+            return response()->json([
+                'error' => [
+                    'code' => 401,
+                    'message' => trans('errors.email_confirmation_awaiting')
+                ]
+            ], 401);
+        }
+
+        return redirect('/register/confirm/awaiting');
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Middleware/ConfirmEmails.php b/app/Http/Middleware/ConfirmEmails.php
deleted file mode 100644 (file)
index 3700e99..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-namespace BookStack\Http\Middleware;
-
-use BookStack\Http\Request;
-use Closure;
-use Illuminate\Contracts\Auth\Guard;
-
-/**
- * Confirms the current user's email address.
- * Must come after any middleware that may log users in.
- */
-class ConfirmEmails
-{
-    /**
-     * The Guard implementation.
-     */
-    protected $auth;
-
-    /**
-     * Create a new ConfirmEmails instance.
-     */
-    public function __construct(Guard $auth)
-    {
-        $this->auth = $auth;
-    }
-
-    /**
-     * Handle an incoming request.
-     */
-    public function handle(Request $request, Closure $next)
-    {
-        if ($this->auth->check()) {
-            $requireConfirmation = (setting('registration-confirmation') || setting('registration-restrict'));
-            if ($requireConfirmation && !$this->auth->user()->email_confirmed) {
-                return $this->errorResponse($request);
-            }
-        }
-
-        return $next($request);
-    }
-
-    /**
-     * Provide an error response for when the current user's email is not confirmed
-     * in a system which requires it.
-     */
-    protected function errorResponse(Request $request)
-    {
-        if ($request->wantsJson()) {
-            return response()->json([
-                'error' => [
-                    'code' => 401,
-                    'message' => trans('errors.email_confirmation_awaiting')
-                ]
-            ], 401);
-        }
-
-        return redirect('/register/confirm/awaiting');
-    }
-}
index 99553e294bfcc529955de68fcab8ea22fe500782..456508d98704dd5132ec32b64204d7efc94d9770 100644 (file)
@@ -2,9 +2,7 @@
 
 namespace BookStack\Http\Middleware;
 
-use BookStack\Http\Request;
 use Closure;
-use Exception;
 use Illuminate\Session\Middleware\StartSession as Middleware;
 
 class StartSessionIfCookieExists extends Middleware
@@ -16,24 +14,9 @@ class StartSessionIfCookieExists extends Middleware
     {
         $sessionCookieName = config('session.cookie');
         if ($request->cookies->has($sessionCookieName)) {
-            $this->decryptSessionCookie($request, $sessionCookieName);
             return parent::handle($request, $next);
         }
 
         return $next($request);
     }
-
-    /**
-     * Attempt decryption of the session cookie.
-     */
-    protected function decryptSessionCookie(Request $request, string $sessionCookieName)
-    {
-        try {
-            $sessionCookie = $request->cookies->get($sessionCookieName);
-            $sessionCookie = decrypt($sessionCookie, false);
-            $request->cookies->set($sessionCookieName, $sessionCookie);
-        } catch (Exception $e) {
-            //
-        }
-    }
 }