X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/e743cd3f606fb8a2e432813f7c84fed1093f68c4..refs/pull/2902/head:/app/Auth/Access/Saml2Service.php diff --git a/app/Auth/Access/Saml2Service.php b/app/Auth/Access/Saml2Service.php index c52dc3a39..339701d27 100644 --- a/app/Auth/Access/Saml2Service.php +++ b/app/Auth/Access/Saml2Service.php @@ -1,9 +1,12 @@ -config = config('saml2'); - $this->userRepo = $userRepo; - $this->user = $user; + $this->registrationService = $registrationService; + $this->loginService = $loginService; } /** * Initiate a login flow. + * * @throws Error */ public function login(): array { $toolKit = $this->getToolkit(); $returnRoute = url('/http/source.bookstackapp.com/saml2/acs'); + return [ 'url' => $toolKit->login($returnRoute, [], false, false, true), - 'id' => $toolKit->getLastRequestID(), + 'id' => $toolKit->getLastRequestID(), ]; } /** * Initiate a logout flow. + * * @throws Error */ public function logout(): array @@ -55,7 +61,11 @@ class Saml2Service extends ExternalAuthService $returnRoute = url('/'); try { - $url = $toolKit->logout($returnRoute, [], null, null, true); + $email = auth()->user()['email']; + $nameIdFormat = env('SAML2_SP_NAME_ID_Format', null); + $nameIdSPNameQualifier = env('SAML2_SP_NAME_ID_SP_NAME_QUALIFIER', null); + + $url = $toolKit->logout($returnRoute, [], $email, null, true, $nameIdFormat, null, $nameIdSPNameQualifier); $id = $toolKit->getLastRequestID(); } catch (Error $error) { if ($error->getCode() !== Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED) { @@ -74,10 +84,12 @@ class Saml2Service extends ExternalAuthService * Process the ACS response from the idp and return the * matching, or new if registration active, user matched to the idp. * Returns null if not authenticated. + * * @throws Error * @throws SamlException * @throws ValidationError * @throws JsonDebugException + * @throws UserRegistrationException */ public function processAcsResponse(?string $requestId): ?User { @@ -87,7 +99,7 @@ class Saml2Service extends ExternalAuthService if (!empty($errors)) { throw new Error( - 'Invalid ACS Response: '.implode(', ', $errors) + 'Invalid ACS Response: ' . implode(', ', $errors) ); } @@ -103,22 +115,26 @@ class Saml2Service extends ExternalAuthService /** * Process a response for the single logout service. + * * @throws Error */ public function processSlsResponse(?string $requestId): ?string { $toolkit = $this->getToolkit(); - $redirect = $toolkit->processSLO(true, $requestId, false, null, true); + $retrieveParametersFromServer = env('SAML2_RETRIEVE_PARAMETERS_FROM_SERVER', false); + + $redirect = $toolkit->processSLO(true, $requestId, $retrieveParametersFromServer, null, true); $errors = $toolkit->getErrors(); if (!empty($errors)) { throw new Error( - 'Invalid SLS Response: '.implode(', ', $errors) + 'Invalid SLS Response: ' . implode(', ', $errors) ); } $this->actionLogout(); + return $redirect; } @@ -133,6 +149,7 @@ class Saml2Service extends ExternalAuthService /** * Get the metadata for this service provider. + * * @throws Error */ public function metadata(): string @@ -144,7 +161,7 @@ class Saml2Service extends ExternalAuthService if (!empty($errors)) { throw new Error( - 'Invalid SP metadata: '.implode(', ', $errors), + 'Invalid SP metadata: ' . implode(', ', $errors), Error::METADATA_SP_INVALID ); } @@ -154,6 +171,7 @@ class Saml2Service extends ExternalAuthService /** * Load the underlying Onelogin SAML2 toolkit. + * * @throws Error * @throws Exception */ @@ -173,6 +191,7 @@ class Saml2Service extends ExternalAuthService $spSettings = $this->loadOneloginServiceProviderDetails(); $settings = array_replace_recursive($settings, $spSettings, $metaDataSettings, $overrides); + return new Auth($settings); } @@ -182,18 +201,18 @@ class Saml2Service extends ExternalAuthService protected function loadOneloginServiceProviderDetails(): array { $spDetails = [ - 'entityId' => url('/http/source.bookstackapp.com/saml2/metadata'), + 'entityId' => url('/http/source.bookstackapp.com/saml2/metadata'), 'assertionConsumerService' => [ 'url' => url('/http/source.bookstackapp.com/saml2/acs'), ], 'singleLogoutService' => [ - 'url' => url('/http/source.bookstackapp.com/saml2/sls') + 'url' => url('/http/source.bookstackapp.com/saml2/sls'), ], ]; return [ 'baseurl' => url('/http/source.bookstackapp.com/saml2'), - 'sp' => $spDetails + 'sp' => $spDetails, ]; } @@ -206,7 +225,7 @@ class Saml2Service extends ExternalAuthService } /** - * Calculate the display name + * Calculate the display name. */ protected function getUserDisplayName(array $samlAttributes, string $defaultValue): string { @@ -256,9 +275,9 @@ class Saml2Service extends ExternalAuthService return [ 'external_id' => $externalId, - 'name' => $this->getUserDisplayName($samlAttributes, $externalId), - 'email' => $email, - 'saml_id' => $samlID, + 'name' => $this->getUserDisplayName($samlAttributes, $externalId), + 'email' => $email, + 'saml_id' => $samlID, ]; } @@ -292,6 +311,7 @@ class Saml2Service extends ExternalAuthService $data = $data[0]; break; } + return $data; } @@ -308,43 +328,26 @@ class Saml2Service extends ExternalAuthService return $defaultValue; } - /** - * Register a user that is authenticated but not already registered. - */ - protected function registerUser(array $userDetails): User - { - // Create an array of the user data to create a new user instance - $userData = [ - 'name' => $userDetails['name'], - 'email' => $userDetails['email'], - 'password' => Str::random(32), - 'external_auth_id' => $userDetails['external_id'], - 'email_confirmed' => true, - ]; - - $existingUser = $this->user->newQuery()->where('email', '=', $userDetails['email'])->first(); - if ($existingUser) { - throw new SamlException(trans('errors.saml_email_exists', ['email' => $userDetails['email']])); - } - - $user = $this->user->newQuery()->forceCreate($userData); - $this->userRepo->attachDefaultRole($user); - $this->userRepo->downloadAndAssignUserAvatar($user); - return $user; - } - /** * Get the user from the database for the specified details. - * @throws SamlException + * + * @throws UserRegistrationException */ protected function getOrRegisterUser(array $userDetails): ?User { - $user = $this->user->newQuery() + $user = User::query() ->where('external_auth_id', '=', $userDetails['external_id']) ->first(); if (is_null($user)) { - $user = $this->registerUser($userDetails); + $userData = [ + 'name' => $userDetails['name'], + 'email' => $userDetails['email'], + 'password' => Str::random(32), + 'external_auth_id' => $userDetails['external_id'], + ]; + + $user = $this->registrationService->registerUser($userData, null, false); } return $user; @@ -353,8 +356,11 @@ class Saml2Service extends ExternalAuthService /** * Process the SAML response for a user. Login the user when * they exist, optionally registering them automatically. + * * @throws SamlException * @throws JsonDebugException + * @throws UserRegistrationException + * @throws StoppedAuthenticationException */ public function processLoginCallback(string $samlID, array $samlAttributes): User { @@ -363,8 +369,8 @@ class Saml2Service extends ExternalAuthService if ($this->config['dump_user_details']) { throw new JsonDebugException([ - 'id_from_idp' => $samlID, - 'attrs_from_idp' => $samlAttributes, + 'id_from_idp' => $samlID, + 'attrs_from_idp' => $samlAttributes, 'attrs_after_parsing' => $userDetails, ]); } @@ -387,7 +393,8 @@ class Saml2Service extends ExternalAuthService $this->syncWithGroups($user, $groups); } - auth()->login($user); + $this->loginService->login($user, 'saml2'); + return $user; } }