X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/aef6eb81e4789f97c7ff23b87295e239c0aead14..refs/pull/3000/head:/app/Http/Controllers/Auth/Saml2Controller.php diff --git a/app/Http/Controllers/Auth/Saml2Controller.php b/app/Http/Controllers/Auth/Saml2Controller.php index c32f19c5e..871abf59f 100644 --- a/app/Http/Controllers/Auth/Saml2Controller.php +++ b/app/Http/Controllers/Auth/Saml2Controller.php @@ -5,10 +5,11 @@ 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; /** @@ -16,8 +17,8 @@ class Saml2Controller extends Controller */ public function __construct(Saml2Service $samlService) { - parent::__construct(); $this->samlService = $samlService; + $this->middleware('guard:saml2'); } /** @@ -51,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', ]); } @@ -64,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(); + } }