]> BookStack Code Mirror - bookstack/blobdiff - app/Http/Controllers/Auth/Saml2Controller.php
Fixed occurances of altered titles in search results
[bookstack] / app / Http / Controllers / Auth / Saml2Controller.php
index 86389412861a033e6c9bc8170f8799cb3c23988a..bd3b25da770ab113e04c2298b6d0b4d3f7b6f907 100644 (file)
@@ -4,10 +4,12 @@ namespace BookStack\Http\Controllers\Auth;
 
 use BookStack\Auth\Access\Saml2Service;
 use BookStack\Http\Controllers\Controller;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Str;
 
 class Saml2Controller extends Controller
 {
-
     protected $samlService;
 
     /**
@@ -15,17 +17,8 @@ class Saml2Controller extends Controller
      */
     public function __construct(Saml2Service $samlService)
     {
-        parent::__construct();
         $this->samlService = $samlService;
-
-        // SAML2 access middleware
-        $this->middleware(function ($request, $next) {
-            if (!config('saml2.enabled')) {
-                $this->showPermissionError();
-            }
-
-            return $next($request);
-        });
+        $this->middleware('guard:saml2');
     }
 
     /**
@@ -44,7 +37,7 @@ class Saml2Controller extends Controller
      */
     public function logout()
     {
-        $logoutDetails = $this->samlService->logout();
+        $logoutDetails = $this->samlService->logout(auth()->user());
 
         if ($logoutDetails['id']) {
             session()->flash('saml2_logout_request_id', $logoutDetails['id']);
@@ -59,8 +52,9 @@ class Saml2Controller extends Controller
     public function metadata()
     {
         $metaData = $this->samlService->metadata();
+
         return response()->make($metaData, 200, [
-            'Content-Type' => 'text/xml'
+            'Content-Type' => 'text/xml',
         ]);
     }
 
@@ -72,25 +66,69 @@ class Saml2Controller extends Controller
     {
         $requestId = session()->pull('saml2_logout_request_id', null);
         $redirect = $this->samlService->processSlsResponse($requestId) ?? '/';
+
         return redirect($redirect);
     }
 
     /**
-     * Assertion Consumer Service.
-     * Processes the SAML response from the IDP.
+     * Assertion Consumer Service start URL. Takes the SAMLResponse from the IDP.
+     * Due to being an external POST request, we likely won't have context of the
+     * current user session due to lax cookies. To work around this we store the
+     * SAMLResponse data and redirect to the processAcs endpoint for the actual
+     * processing of the request with proper context of the user session.
      */
-    public function acs()
+    public function startAcs(Request $request)
     {
-        $requestId = session()->pull('saml2_request_id', null);
+        // Note: This is a bit of a hack to prevent a session being stored
+        // on the response of this request. Within Laravel7+ this could instead
+        // be done via removing the StartSession middleware from the route.
+        config()->set('session.driver', 'array');
+
+        $samlResponse = $request->get('SAMLResponse', null);
 
-        $user = $this->samlService->processAcsResponse($requestId);
-        if ($user === null) {
+        if (empty($samlResponse)) {
             $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
+
             return redirect('/login');
         }
 
-        session()->put('last_login_type', 'saml2');
-        return redirect()->intended();
+        $acsId = Str::random(16);
+        $cacheKey = 'saml2_acs:' . $acsId;
+        cache()->set($cacheKey, encrypt($samlResponse), 10);
+
+        return redirect()->guest('/saml2/acs?id=' . $acsId);
     }
 
+    /**
+     * Assertion Consumer Service process endpoint.
+     * Processes the SAML response from the IDP with context of the current session.
+     * Takes the SAML request from the cache, added by the startAcs method above.
+     */
+    public function processAcs(Request $request)
+    {
+        $acsId = $request->get('id', null);
+        $cacheKey = 'saml2_acs:' . $acsId;
+        $samlResponse = null;
+
+        try {
+            $samlResponse = decrypt(cache()->pull($cacheKey));
+        } catch (\Exception $exception) {
+        }
+        $requestId = session()->pull('saml2_request_id', 'unset');
+
+        if (empty($acsId) || empty($samlResponse)) {
+            $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
+
+            return redirect('/login');
+        }
+
+        $user = $this->samlService->processAcsResponse($requestId, $samlResponse);
+        if (is_null($user)) {
+            $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
+
+            return redirect('/login');
+        }
+
+        return redirect()->intended();
+    }
 }