mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-10-28 05:14:50 +03:00
.github
app
Access
Controllers
ConfirmEmailController.php
ForgotPasswordController.php
HandlesPartialLogins.php
LoginController.php
MfaBackupCodesController.php
MfaController.php
MfaTotpController.php
OidcController.php
RegisterController.php
ResetPasswordController.php
Saml2Controller.php
SocialController.php
ThrottlesLogins.php
UserInviteController.php
Guards
Mfa
Notifications
Oidc
EmailConfirmationService.php
ExternalBaseUserProvider.php
GroupSyncService.php
Ldap.php
LdapService.php
LoginService.php
RegistrationService.php
Saml2Service.php
SocialAccount.php
SocialAuthService.php
SocialDriverManager.php
UserInviteException.php
UserInviteService.php
UserTokenService.php
Activity
Api
App
Config
Console
Entities
Exceptions
Exports
Facades
Http
Permissions
References
Search
Settings
Sorting
Theming
Translation
Uploads
Users
Util
bootstrap
database
dev
lang
public
resources
routes
storage
tests
themes
.env.example
.env.example.complete
.gitattributes
.gitignore
LICENSE
artisan
bookstack-system-cli
composer.json
composer.lock
crowdin.yml
docker-compose.yml
eslint.config.mjs
jest.config.ts
package-lock.json
package.json
phpcs.xml
phpstan.neon.dist
phpunit.xml
readme.md
tsconfig.json
version
This changes the point-of-logout to be within the initial part of the SAML logout flow, as per 5.3.2 of the SAML spec, processing step 2. This also improves the logout redirect handling to use the global redirect suggestion so that auto-login handling is properly taken into account. Added tests to cover. Manual testing performed against keycloak. For #4713
129 lines
3.6 KiB
PHP
129 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace BookStack\Access\Controllers;
|
|
|
|
use BookStack\Access\Saml2Service;
|
|
use BookStack\Http\Controller;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Str;
|
|
|
|
class Saml2Controller extends Controller
|
|
{
|
|
public function __construct(
|
|
protected Saml2Service $samlService
|
|
) {
|
|
$this->middleware('guard:saml2');
|
|
}
|
|
|
|
/**
|
|
* Start the login flow via SAML2.
|
|
*/
|
|
public function login()
|
|
{
|
|
$loginDetails = $this->samlService->login();
|
|
session()->flash('saml2_request_id', $loginDetails['id']);
|
|
|
|
return redirect($loginDetails['url']);
|
|
}
|
|
|
|
/**
|
|
* Start the logout flow via SAML2.
|
|
*/
|
|
public function logout()
|
|
{
|
|
$user = user();
|
|
if ($user->isGuest()) {
|
|
return redirect('/login');
|
|
}
|
|
|
|
$logoutDetails = $this->samlService->logout($user);
|
|
|
|
if ($logoutDetails['id']) {
|
|
session()->flash('saml2_logout_request_id', $logoutDetails['id']);
|
|
}
|
|
|
|
return redirect($logoutDetails['url']);
|
|
}
|
|
|
|
/*
|
|
* Get the metadata for this SAML2 service provider.
|
|
*/
|
|
public function metadata()
|
|
{
|
|
$metaData = $this->samlService->metadata();
|
|
|
|
return response()->make($metaData, 200, [
|
|
'Content-Type' => 'text/xml',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Single logout service.
|
|
* Handle logout requests and responses.
|
|
*/
|
|
public function sls()
|
|
{
|
|
$requestId = session()->pull('saml2_logout_request_id', null);
|
|
$redirect = $this->samlService->processSlsResponse($requestId);
|
|
|
|
return redirect($redirect);
|
|
}
|
|
|
|
/**
|
|
* 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 startAcs(Request $request)
|
|
{
|
|
$samlResponse = $request->get('SAMLResponse', null);
|
|
|
|
if (empty($samlResponse)) {
|
|
$this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
|
|
|
|
return redirect('/login');
|
|
}
|
|
|
|
$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', null);
|
|
|
|
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();
|
|
}
|
|
}
|